From b8982d4b49d385b94ecfb00ac5838d3b98f46636 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sat, 4 Feb 2023 03:11:49 +0100 Subject: Performance improvements by replacing hash depth calculation with shifting, improving code quality. + formatting --- .../refinery/store/map/internal/ImmutableNode.java | 65 ++++----- .../refinery/store/map/internal/MutableNode.java | 156 ++++++++++----------- .../tools/refinery/store/map/internal/Node.java | 87 ++++++++---- .../store/map/internal/VersionedMapImpl.java | 32 ++++- 4 files changed, 193 insertions(+), 147 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java index 9397dede..e437aceb 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java @@ -42,8 +42,7 @@ public class ImmutableNode extends Node { * available. * @return an immutable version of the input node. */ - static ImmutableNode constructImmutable(MutableNode node, - Map, ImmutableNode> cache) { + static ImmutableNode constructImmutable(MutableNode node, Map, ImmutableNode> cache) { // 1. try to return from cache if (cache != null) { ImmutableNode cachedResult = cache.get(node); @@ -75,8 +74,7 @@ public class ImmutableNode extends Node { resultContent[datas * 2 + 1] = node.content[i * 2 + 1]; datas++; } else { - @SuppressWarnings("unchecked") - var subnode = (Node) node.content[i * 2 + 1]; + @SuppressWarnings("unchecked") var subnode = (Node) node.content[i * 2 + 1]; if (subnode != null) { ImmutableNode immutableSubnode = subnode.toImmutable(cache); resultNodeMap |= bitposition; @@ -107,11 +105,9 @@ public class ImmutableNode extends Node { // If the key is stored as a data if ((dataMap & bitposition) != 0) { int keyIndex = 2 * index(dataMap, bitposition); - @SuppressWarnings("unchecked") - K keyCandidate = (K) content[keyIndex]; + @SuppressWarnings("unchecked") K keyCandidate = (K) content[keyIndex]; if (keyCandidate.equals(key)) { - @SuppressWarnings("unchecked") - V value = (V) content[keyIndex + 1]; + @SuppressWarnings("unchecked") V value = (V) content[keyIndex + 1]; return value; } else { return defaultValue; @@ -120,9 +116,8 @@ public class ImmutableNode extends Node { // the key is stored as a node else if ((nodeMap & bitposition) != 0) { int keyIndex = content.length - 1 - index(nodeMap, bitposition); - @SuppressWarnings("unchecked") - var subNode = (ImmutableNode) content[keyIndex]; - int newDepth = depth + 1; + @SuppressWarnings("unchecked") var subNode = (ImmutableNode) content[keyIndex]; + int newDepth = incrementDepth(depth); int newHash = newHash(hashProvider, key, hash, newDepth); return subNode.getValue(key, hashProvider, defaultValue, newHash, newDepth); } @@ -133,14 +128,12 @@ public class ImmutableNode extends Node { } @Override - public Node putValue(K key, V value, OldValueBox oldValue, ContinousHashProvider hashProvider, - V defaultValue, int hash, int depth) { + public Node putValue(K key, V value, OldValueBox oldValue, ContinousHashProvider hashProvider, V defaultValue, int hash, int depth) { int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); int bitposition = 1 << selectedHashFragment; if ((dataMap & bitposition) != 0) { int keyIndex = 2 * index(dataMap, bitposition); - @SuppressWarnings("unchecked") - K keyCandidate = (K) content[keyIndex]; + @SuppressWarnings("unchecked") K keyCandidate = (K) content[keyIndex]; if (keyCandidate.equals(key)) { if (value == defaultValue) { // delete @@ -151,7 +144,7 @@ public class ImmutableNode extends Node { oldValue.setOldValue(value); return this; } else { - // update existing nodeId + // update existing value MutableNode mutable = this.toMutable(); return mutable.updateValue(value, oldValue, selectedHashFragment); } @@ -161,16 +154,15 @@ public class ImmutableNode extends Node { oldValue.setOldValue(defaultValue); return this; } else { - // add new key + nodeId + // add new key + value MutableNode mutable = this.toMutable(); return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); } } } else if ((nodeMap & bitposition) != 0) { int keyIndex = content.length - 1 - index(nodeMap, bitposition); - @SuppressWarnings("unchecked") - var subNode = (ImmutableNode) content[keyIndex]; - int newDepth = depth + 1; + @SuppressWarnings("unchecked") var subNode = (ImmutableNode) content[keyIndex]; + int newDepth = incrementDepth(depth); int newHash = newHash(hashProvider, key, hash, newDepth); var newsubNode = subNode.putValue(key, value, oldValue, hashProvider, defaultValue, newHash, newDepth); @@ -179,10 +171,11 @@ public class ImmutableNode extends Node { return this; } else { MutableNode mutable = toMutable(); - return mutable.updateWithSubNode(selectedHashFragment, newsubNode, value.equals(defaultValue)); + return mutable.updateWithSubNode(selectedHashFragment, newsubNode, + (value == null && defaultValue == null) || (value != null && value.equals(defaultValue))); } } else { - // add new key + nodeId + // add new key + value MutableNode mutable = this.toMutable(); return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); } @@ -192,8 +185,7 @@ public class ImmutableNode extends Node { public long getSize() { int result = Integer.bitCount(this.dataMap); for (int subnodeIndex = 0; subnodeIndex < Integer.bitCount(this.nodeMap); subnodeIndex++) { - @SuppressWarnings("unchecked") - var subnode = (ImmutableNode) this.content[this.content.length - 1 - subnodeIndex]; + @SuppressWarnings("unchecked") var subnode = (ImmutableNode) this.content[this.content.length - 1 - subnodeIndex]; result += subnode.getSize(); } return result; @@ -289,10 +281,9 @@ public class ImmutableNode extends Node { int nodeMask = 1; for (int i = 0; i < FACTOR; i++) { if ((nodeMask & nodeMap) != 0) { - @SuppressWarnings("unchecked") - Node subNode = (Node) content[content.length - 1 - index(nodeMap, nodeMask)]; + @SuppressWarnings("unchecked") Node subNode = (Node) content[content.length - 1 - index(nodeMap, nodeMask)]; builder.append("\n"); - subNode.prettyPrint(builder, depth + 1, i); + subNode.prettyPrint(builder, incrementDepth(depth), i); } nodeMask <<= 1; } @@ -310,12 +301,11 @@ public class ImmutableNode extends Node { // check subnodes for (int i = 0; i < Integer.bitCount(nodeMap); i++) { - @SuppressWarnings("unchecked") - var subnode = (Node) this.content[this.content.length - 1 - i]; + @SuppressWarnings("unchecked") var subnode = (Node) this.content[this.content.length - 1 - i]; if (!(subnode instanceof ImmutableNode)) { throw new IllegalStateException("Immutable node contains mutable subnodes!"); } else { - subnode.checkIntegrity(hashProvider, defaultValue, depth + 1); + subnode.checkIntegrity(hashProvider, defaultValue, incrementDepth(depth)); } } } @@ -327,13 +317,10 @@ public class ImmutableNode extends Node { @Override public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; + if (this == obj) return true; + if (obj == null) return false; if (obj instanceof ImmutableNode other) { - return precalculatedHash == other.precalculatedHash && dataMap == other.dataMap && nodeMap == other.nodeMap - && Arrays.deepEquals(content, other.content); + return precalculatedHash == other.precalculatedHash && dataMap == other.dataMap && nodeMap == other.nodeMap && Arrays.deepEquals(content, other.content); } else if (obj instanceof MutableNode mutableObj) { return ImmutableNode.compareImmutableMutable(this, mutableObj); } else { @@ -351,12 +338,10 @@ public class ImmutableNode extends Node { if (key != null) { // Check whether a new Key-Value pair can fit into the immutable container if (datas * 2 + nodes + 2 <= immutableLength) { - if (!immutable.content[datas * 2].equals(key) - || !immutable.content[datas * 2 + 1].equals(mutable.content[i * 2 + 1])) { + if (!immutable.content[datas * 2].equals(key) || !immutable.content[datas * 2 + 1].equals(mutable.content[i * 2 + 1])) { return false; } - } else - return false; + } else return false; datas++; } else { var mutableSubnode = (Node) mutable.content[i * 2 + 1]; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java index 7c3cf7e8..9d15a0d7 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java @@ -7,15 +7,15 @@ import tools.refinery.store.map.ContinousHashProvider; public class MutableNode extends Node { int cachedHash; + protected boolean cachedHashValid; protected Object[] content; protected MutableNode() { this.content = new Object[2 * FACTOR]; - updateHash(); + invalidateHash(); } - public static MutableNode initialize(K key, V value, ContinousHashProvider hashProvider, - V defaultValue) { + public static MutableNode initialize(K key, V value, ContinousHashProvider hashProvider, V defaultValue) { if (value == defaultValue) { return null; } else { @@ -24,7 +24,7 @@ public class MutableNode extends Node { MutableNode res = new MutableNode<>(); res.content[2 * fragment] = key; res.content[2 * fragment + 1] = value; - res.updateHash(); + res.invalidateHash(); return res; } } @@ -32,7 +32,7 @@ public class MutableNode extends Node { /** * Constructs a {@link MutableNode} as a copy of an {@link ImmutableNode} * - * @param node + * @param node to be transformed */ protected MutableNode(ImmutableNode node) { this.content = new Object[2 * FACTOR]; @@ -49,27 +49,24 @@ public class MutableNode extends Node { nodeUsed++; } } - this.cachedHash = node.hashCode(); + this.cachedHashValid = false; } @Override public V getValue(K key, ContinousHashProvider hashProvider, V defaultValue, int hash, int depth) { int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); - @SuppressWarnings("unchecked") - K keyCandidate = (K) this.content[2 * selectedHashFragment]; + @SuppressWarnings("unchecked") K keyCandidate = (K) this.content[2 * selectedHashFragment]; if (keyCandidate != null) { if (keyCandidate.equals(key)) { - @SuppressWarnings("unchecked") - V value = (V) this.content[2 * selectedHashFragment + 1]; + @SuppressWarnings("unchecked") V value = (V) this.content[2 * selectedHashFragment + 1]; return value; } else { return defaultValue; } } else { - @SuppressWarnings("unchecked") - var nodeCandidate = (Node) content[2 * selectedHashFragment + 1]; + @SuppressWarnings("unchecked") var nodeCandidate = (Node) content[2 * selectedHashFragment + 1]; if (nodeCandidate != null) { - int newDepth = depth + 1; + int newDepth = incrementDepth(depth); int newHash = newHash(hashProvider, key, hash, newDepth); return nodeCandidate.getValue(key, hashProvider, defaultValue, newHash, newDepth); } else { @@ -79,11 +76,9 @@ public class MutableNode extends Node { } @Override - public Node putValue(K key, V value, OldValueBox oldValueBox, ContinousHashProvider hashProvider, - V defaultValue, int hash, int depth) { + public Node putValue(K key, V value, OldValueBox oldValueBox, ContinousHashProvider hashProvider, V defaultValue, int hash, int depth) { int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); - @SuppressWarnings("unchecked") - K keyCandidate = (K) content[2 * selectedHashFragment]; + @SuppressWarnings("unchecked") K keyCandidate = (K) content[2 * selectedHashFragment]; if (keyCandidate != null) { // If has key if (keyCandidate.equals(key)) { @@ -107,18 +102,17 @@ public class MutableNode extends Node { } } } else { - // If it does not have key, check for nodeId - @SuppressWarnings("unchecked") - var nodeCandidate = (Node) content[2 * selectedHashFragment + 1]; + // If it does not have key, check for value + @SuppressWarnings("unchecked") var nodeCandidate = (Node) content[2 * selectedHashFragment + 1]; if (nodeCandidate != null) { - // If it has nodeId, it is a subnode -> upate that - var newNode = nodeCandidate.putValue(key, value, oldValueBox, hashProvider, defaultValue, - newHash(hashProvider, key, hash, depth + 1), depth + 1); - return updateWithSubNode(selectedHashFragment, newNode, value.equals(defaultValue)); + // If it has value, it is a subnode -> upate that + int newDepth = incrementDepth(depth); + var newNode = nodeCandidate.putValue(key, value, oldValueBox, hashProvider, defaultValue, newHash(hashProvider, key, hash, newDepth), newDepth); + return updateWithSubNode(selectedHashFragment, newNode, (value == null && defaultValue == null) || (value != null && value.equals(defaultValue))); } else { - // If it does not have nodeId, put it in the empty place + // If it does not have value, put it in the empty place if (value == defaultValue) { - // dont need to add new key-nodeId pair + // dont need to add new key-value pair oldValueBox.setOldValue(defaultValue); return this; } else { @@ -133,30 +127,31 @@ public class MutableNode extends Node { content[2 * selectedHashFragment] = key; oldValueBox.setOldValue(defaultValue); content[2 * selectedHashFragment + 1] = value; - updateHash(); + invalidateHash(); return this; } /** - * Updates an entry in a selected hash-fragment to a non-default nodeId. + * Updates an entry in a selected hash-fragment to a non-default value. * - * @param value - * @param selectedHashFragment - * @return + * @param value new value + * @param selectedHashFragment position of the value + * @return updated node */ @SuppressWarnings("unchecked") Node updateValue(V value, OldValueBox oldValue, int selectedHashFragment) { oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]); content[2 * selectedHashFragment + 1] = value; - updateHash(); + invalidateHash(); return this; } /** + * Updates an entry in a selected hash-fragment with a subtree. * - * @param selectedHashFragment - * @param newNode - * @return + * @param selectedHashFragment position of the value + * @param newNode the subtree + * @return updated node */ Node updateWithSubNode(int selectedHashFragment, Node newNode, boolean deletionHappened) { if (deletionHappened) { @@ -164,7 +159,7 @@ public class MutableNode extends Node { // Check whether this node become empty content[2 * selectedHashFragment + 1] = null; // i.e. the new node if (hasContent()) { - updateHash(); + invalidateHash(); return this; } else { return null; @@ -178,7 +173,7 @@ public class MutableNode extends Node { // orphan subnode data is replaced with data content[2 * selectedHashFragment] = immutableNewNode.content[orphaned * 2]; content[2 * selectedHashFragment + 1] = immutableNewNode.content[orphaned * 2 + 1]; - updateHash(); + invalidateHash(); return this; } } @@ -186,15 +181,13 @@ public class MutableNode extends Node { } // normal behaviour content[2 * selectedHashFragment + 1] = newNode; - updateHash(); + invalidateHash(); return this; - } private boolean hasContent() { for (Object element : this.content) { - if (element != null) - return true; + if (element != null) return true; } return false; } @@ -221,23 +214,20 @@ public class MutableNode extends Node { } @SuppressWarnings("unchecked") - private Node moveDownAndSplit(ContinousHashProvider hashProvider, K newKey, V newValue, - K previousKey, int hashOfNewKey, int depth, int selectedHashFragmentOfCurrentDepth) { + private Node moveDownAndSplit(ContinousHashProvider hashProvider, K newKey, V newValue, K previousKey, int hashOfNewKey, int depth, int selectedHashFragmentOfCurrentDepth) { V previousValue = (V) content[2 * selectedHashFragmentOfCurrentDepth + 1]; - MutableNode newSubNode = newNodeWithTwoEntries(hashProvider, previousKey, previousValue, - hashProvider.getHash(previousKey, hashDepth(depth)), newKey, newValue, hashOfNewKey, depth + 1); + MutableNode newSubNode = newNodeWithTwoEntries(hashProvider, previousKey, previousValue, hashProvider.getHash(previousKey, hashDepth(depth)), newKey, newValue, hashOfNewKey, incrementDepth(depth)); content[2 * selectedHashFragmentOfCurrentDepth] = null; content[2 * selectedHashFragmentOfCurrentDepth + 1] = newSubNode; - updateHash(); + invalidateHash(); return this; } // Pass everything as parameters for performance. @SuppressWarnings("squid:S107") - private MutableNode newNodeWithTwoEntries(ContinousHashProvider hashProvider, K key1, V value1, - int oldHash1, K key2, V value2, int oldHash2, int newdepth) { + private MutableNode newNodeWithTwoEntries(ContinousHashProvider hashProvider, K key1, V value1, int oldHash1, K key2, V value2, int oldHash2, int newdepth) { int newHash1 = newHash(hashProvider, key1, oldHash1, newdepth); int newHash2 = newHash(hashProvider, key2, oldHash2, newdepth); int newFragment1 = hashFragment(newHash1, shiftDepth(newdepth)); @@ -251,11 +241,10 @@ public class MutableNode extends Node { subNode.content[newFragment2 * 2] = key2; subNode.content[newFragment2 * 2 + 1] = value2; } else { - MutableNode subSubNode = newNodeWithTwoEntries(hashProvider, key1, value1, newHash1, key2, value2, - newHash2, newdepth + 1); + MutableNode subSubNode = newNodeWithTwoEntries(hashProvider, key1, value1, newHash1, key2, value2, newHash2, incrementDepth(newdepth)); subNode.content[newFragment1 * 2 + 1] = subSubNode; } - subNode.updateHash(); + subNode.invalidateHash(); return subNode; } @@ -265,7 +254,7 @@ public class MutableNode extends Node { oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]); content[2 * selectedHashFragment + 1] = null; if (hasContent()) { - updateHash(); + invalidateHash(); return this; } else { return null; @@ -374,10 +363,9 @@ public class MutableNode extends Node { // print subnodes for (int i = 0; i < FACTOR; i++) { if (content[2 * i] == null && content[2 * i + 1] != null) { - @SuppressWarnings("unchecked") - Node subNode = (Node) content[2 * i + 1]; + @SuppressWarnings("unchecked") Node subNode = (Node) content[2 * i + 1]; builder.append("\n"); - subNode.prettyPrint(builder, depth + 1, i); + subNode.prettyPrint(builder, incrementDepth(depth), i); } } } @@ -394,57 +382,69 @@ public class MutableNode extends Node { // check the place of data for (int i = 0; i < FACTOR; i++) { if (this.content[2 * i] != null) { - @SuppressWarnings("unchecked") - K key = (K) this.content[2 * i]; - @SuppressWarnings("unchecked") - V value = (V) this.content[2 * i + 1]; + @SuppressWarnings("unchecked") K key = (K) this.content[2 * i]; + @SuppressWarnings("unchecked") V value = (V) this.content[2 * i + 1]; if (value == defaultValue) { - throw new IllegalStateException("Node contains default nodeId!"); + throw new IllegalStateException("Node contains default value!"); } int hashCode = hashProvider.getHash(key, hashDepth(depth)); int shiftDepth = shiftDepth(depth); int selectedHashFragment = hashFragment(hashCode, shiftDepth); if (i != selectedHashFragment) { - throw new IllegalStateException("Key " + key + " with hash code " + hashCode - + " is in bad place! Fragment=" + selectedHashFragment + ", Place=" + i); + throw new IllegalStateException("Key " + key + " with hash code " + hashCode + " is in bad place! Fragment=" + selectedHashFragment + ", Place=" + i); } } } // check subnodes for (int i = 0; i < FACTOR; i++) { if (this.content[2 * i + 1] != null && this.content[2 * i] == null) { - @SuppressWarnings("unchecked") - var subNode = (Node) this.content[2 * i + 1]; - subNode.checkIntegrity(hashProvider, defaultValue, depth + 1); + @SuppressWarnings("unchecked") var subNode = (Node) this.content[2 * i + 1]; + subNode.checkIntegrity(hashProvider, defaultValue, incrementDepth(depth)); } } // check the hash - int oldHash = this.cachedHash; - updateHash(); - int newHash = this.cachedHash; - if (oldHash != newHash) { - throw new IllegalStateException("Hash code was not up to date! (old=" + oldHash + ",new=" + newHash + ")"); + if (cachedHashValid) { + int oldHash = this.cachedHash; + invalidateHash(); + int newHash = hashCode(); + if (oldHash != newHash) { + throw new IllegalStateException("Hash code was not up to date! (old=" + oldHash + ",new=" + newHash + ")"); + } } } - protected void updateHash() { - this.cachedHash = Arrays.hashCode(content); + protected void invalidateHash() { + this.cachedHashValid = false; } @Override public int hashCode() { - return this.cachedHash; + if (this.cachedHashValid) { + return this.cachedHash; + } else { + this.cachedHash = Arrays.hashCode(content); + this.cachedHashValid = true; + return this.cachedHash; + } } @Override public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; + if (this == obj) return true; + if (obj == null) return false; if (obj instanceof MutableNode mutableObj) { - return Arrays.deepEquals(this.content, mutableObj.content); + if (obj.hashCode() != this.hashCode()) { + return false; + } else { + for (int i = 0; i < FACTOR * 2; i++) { + Object thisContent = this.content[i]; + if (thisContent != null && !thisContent.equals(mutableObj.content[i])) { + return false; + } + } + return true; + } } else if (obj instanceof ImmutableNode immutableObj) { return ImmutableNode.compareImmutableMutable(immutableObj, this); } else { diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java index 2260cd5b..c49be280 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java @@ -4,82 +4,121 @@ import java.util.Map; import tools.refinery.store.map.ContinousHashProvider; -public abstract class Node{ +public abstract class Node { public static final int BRANCHING_FACTOR_BITS = 5; - public static final int FACTOR = 1<> FACTOR_SELECTION_BITS; } /** * Calculates the which segment of a single hash should be used. + * * @param depth The depth of the node in the tree. * @return The segment of a hash code. */ protected static int shiftDepth(int depth) { - return depth%NUMBER_OF_FACTORS; + return depth & FACTOR_SELECTION_MASK; } + /** * Selects a segments from a complete hash for a given depth. - * @param hash A complete hash. + * + * @param hash A complete hash. * @param shiftDepth The index of the segment. * @return The segment as an integer. */ protected static int hashFragment(int hash, int shiftDepth) { - if(shiftDepth<0 || Node.NUMBER_OF_FACTORS= ContinousHashProvider.MAX_PRACTICAL_DEPTH) { + throw new IllegalArgumentException( + "Key " + key + " have the clashing hashcode over the practical depth limitation (" + + ContinousHashProvider.MAX_PRACTICAL_DEPTH + ")!"); + } + return hashProvider.getHash(key, hashDepth); + } else { + return hash; } - return depth%NUMBER_OF_FACTORS == 0? - hashProvider.getHash(key, hashDepth) : - hash; } + public abstract V getValue(K key, ContinousHashProvider hashProvider, V defaultValue, int hash, + int depth); + + public abstract Node putValue(K key, V value, OldValueBox old, + ContinousHashProvider hashProvider, V defaultValue, int hash, int depth); - public abstract V getValue(K key, ContinousHashProvider hashProvider, V defaultValue, int hash, int depth); - public abstract Node putValue(K key, V value, OldValueBox old, ContinousHashProvider hashProvider, V defaultValue, int hash, int depth); public abstract long getSize(); abstract MutableNode toMutable(); - public abstract ImmutableNode toImmutable( - Map,ImmutableNode> cache); + + public abstract ImmutableNode toImmutable(Map, ImmutableNode> cache); + protected abstract MutableNode isMutable(); + /** * Moves a {@link MapCursor} to its next position. + * * @param cursor the cursor - * @return Whether there was a next nodeId to move on. + * @return Whether there was a next value to move on. */ - abstract boolean moveToNext(MapCursor cursor); + abstract boolean moveToNext(MapCursor cursor); ///////// FOR printing public abstract void prettyPrint(StringBuilder builder, int depth, int code); + @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); prettyPrint(stringBuilder, 0, -1); return stringBuilder.toString(); } - public void checkIntegrity(ContinousHashProvider hashProvider, V defaultValue, int depth) {} + public void checkIntegrity(ContinousHashProvider hashProvider, V defaultValue, int depth) { + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java index 301bcb95..57256472 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java @@ -18,7 +18,6 @@ public class VersionedMapImpl implements VersionedMap { protected final VersionedMapStoreImpl store; protected final ContinousHashProvider hashProvider; - protected final V defaultValue; protected Node root; @@ -109,6 +108,7 @@ public class VersionedMapImpl implements VersionedMap { @Override public DiffCursor getDiffCursor(long toVersion) { + Cursor fromCursor = this.getAll(); VersionedMap toMap = this.store.createMap(toVersion); Cursor toCursor = toMap.getAll(); @@ -131,13 +131,35 @@ public class VersionedMapImpl implements VersionedMap { root = this.store.revert(state); } - public void prettyPrint() { - StringBuilder s = new StringBuilder(); + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((root == null) ? 0 : root.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + VersionedMapImpl other = (VersionedMapImpl) obj; + if (root == null) { + return other.root == null; + } else return root.equals(other.root); + } + + public String prettyPrint() { if (this.root != null) { + StringBuilder s = new StringBuilder(); this.root.prettyPrint(s, 0, -1); - System.out.println(s.toString()); + return s.toString(); } else { - System.out.println("empty tree"); + return "empty tree"; } } -- cgit v1.2.3-54-g00ecf From 15dcbeff0e28f0d6d3da8702b24f60ac52686609 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sun, 5 Feb 2023 00:13:13 +0100 Subject: Code quality improvements in nodes printer appending + empty node stack checking --- .../main/java/tools/refinery/store/map/internal/ImmutableNode.java | 7 ++++--- .../main/java/tools/refinery/store/map/internal/MutableNode.java | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java index e437aceb..914bab08 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java @@ -225,6 +225,9 @@ public class ImmutableNode extends Node { // 2. look inside the subnodes int nodes = Integer.bitCount(this.nodeMap); + if(cursor.nodeIndexStack.peek()==null) { + throw new IllegalStateException("Cursor moved to the next state when the state is empty."); + } int newNodeIndex = cursor.nodeIndexStack.peek() + 1; if (newNodeIndex < nodes) { // 2.1 found next subnode, move down to the subnode @@ -252,9 +255,7 @@ public class ImmutableNode extends Node { @Override public void prettyPrint(StringBuilder builder, int depth, int code) { - for (int i = 0; i < depth; i++) { - builder.append("\t"); - } + builder.append("\t".repeat(Math.max(0, depth))); if (code >= 0) { builder.append(code); builder.append(":"); diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java index 9d15a0d7..cdc66a10 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java @@ -306,6 +306,9 @@ public class MutableNode extends Node { } // 2. look inside the subnodes + if(cursor.nodeIndexStack.peek()==null) { + throw new IllegalStateException("Cursor moved to the next state when the state is empty."); + } for (int index = cursor.nodeIndexStack.peek() + 1; index < FACTOR; index++) { if (this.content[index * 2] == null && this.content[index * 2 + 1] != null) { // 2.1 found next subnode, move down to the subnode @@ -335,9 +338,7 @@ public class MutableNode extends Node { @Override public void prettyPrint(StringBuilder builder, int depth, int code) { - for (int i = 0; i < depth; i++) { - builder.append("\t"); - } + builder.append("\t".repeat(Math.max(0, depth))); if (code >= 0) { builder.append(code); builder.append(":"); -- cgit v1.2.3-54-g00ecf From f90f42e8834964b4e3f5a3504d896e64995b9a01 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sun, 5 Feb 2023 00:37:55 +0100 Subject: Code quality improvements in cursors --- .../java/tools/refinery/store/map/internal/HashClash.java | 4 ++-- .../java/tools/refinery/store/map/internal/MapCursor.java | 12 +++++++----- .../tools/refinery/store/map/internal/MapDiffCursor.java | 13 ++++++------- 3 files changed, 15 insertions(+), 14 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java index 5402ed4a..0806c486 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java @@ -5,12 +5,12 @@ enum HashClash { * Not stuck. */ NONE, - + /** * Clashed, next we should return the key of cursor 1. */ STUCK_CURSOR_1, - + /** * Clashed, next we should return the key of cursor 2. */ diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java index 91a71e3d..f874137b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java @@ -96,7 +96,7 @@ public class MapCursor implements Cursor { return Set.of(this.map); } - public static boolean sameSubnode(MapCursor cursor1, MapCursor cursor2) { + public static boolean sameSubNode(MapCursor cursor1, MapCursor cursor2) { Node nodeOfCursor1 = cursor1.nodeStack.peek(); Node nodeOfCursor2 = cursor2.nodeStack.peek(); if (nodeOfCursor1 != null && nodeOfCursor2 != null) { @@ -107,10 +107,12 @@ public class MapCursor implements Cursor { } /** - * @param - * @param - * @param cursor1 - * @param cursor2 + * Compares the state of two cursors started on two @{@link VersionedMap of the }same + * {@link tools.refinery.store.map.VersionedMapStore}. + * @param Key type + * @param Value type + * @param cursor1 first cursor + * @param cursor2 second cursor * @return Positive number if cursor 1 is behind, negative number if cursor 2 is behind, and 0 if they are at the * same position. */ diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java index 9cd78113..beaff13b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java @@ -131,22 +131,21 @@ public class MapDiffCursor implements DiffCursor, Cursor { protected void resolveHashClashWithSecondEntry() { switch (this.hashClash) { - case STUCK_CURSOR_1: + case STUCK_CURSOR_1 -> { this.hashClash = HashClash.NONE; this.cursorRelation = 0; this.key = cursor1.key; this.fromValue = cursor1.value; this.toValue = defaultValue; - break; - case STUCK_CURSOR_2: + } + case STUCK_CURSOR_2 -> { this.hashClash = HashClash.NONE; this.cursorRelation = 0; this.key = cursor2.key; this.fromValue = defaultValue; this.toValue = cursor2.value; - break; - default: - throw new IllegalArgumentException("Inconsistent compare result for diffcursor"); + } + default -> throw new IllegalArgumentException("Inconsistent compare result for diffcursor"); } } @@ -189,7 +188,7 @@ public class MapDiffCursor implements DiffCursor, Cursor { boolean lastResult = true; do { changed = false; - if (MapCursor.sameSubnode(cursor1, cursor2)) { + if (MapCursor.sameSubNode(cursor1, cursor2)) { lastResult = skipNode(); changed = true; } -- cgit v1.2.3-54-g00ecf From 354ff11cfa8bc2baf879a61ede8a3afebdeeabda Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sun, 5 Feb 2023 01:32:16 +0100 Subject: DiffCursor value comparison support for null values. in case null != default --- .../tools/refinery/store/map/internal/MapDiffCursor.java | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java index beaff13b..6c076ce5 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java @@ -5,6 +5,7 @@ import tools.refinery.store.map.ContinousHashProvider; import tools.refinery.store.map.Cursor; import tools.refinery.store.map.DiffCursor; +import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -121,7 +122,7 @@ public class MapDiffCursor implements DiffCursor, Cursor { this.fromValue = defaultValue; this.toValue = cursor2.value; } else { - throw new IllegalArgumentException("Inconsistent compare result for diffcursor"); + throw new IllegalArgumentException("Inconsistent compare result for diffCursor"); } } @@ -145,16 +146,17 @@ public class MapDiffCursor implements DiffCursor, Cursor { this.fromValue = defaultValue; this.toValue = cursor2.value; } - default -> throw new IllegalArgumentException("Inconsistent compare result for diffcursor"); + default -> throw new IllegalArgumentException("Inconsistent compare result for diffCursor"); } } + /** + * Checks if two states has the same values, i.e., there is no difference. + * @return whether two states has the same values + */ protected boolean sameValues() { - if (this.fromValue == null) { - return this.toValue == null; - } else { - return this.fromValue.equals(this.toValue); - } + if(cursor1.isTerminated() || cursor2.isTerminated()) return false; + else return Objects.equals(this.fromValue, this.toValue); } protected boolean moveOne() { -- cgit v1.2.3-54-g00ecf From c5fe88455146ca948b28f85b39b1554d99529b20 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sun, 5 Feb 2023 01:33:46 +0100 Subject: Cursor comparison bugfix with empty cursors (and null values). --- .../main/java/tools/refinery/store/map/internal/MapCursor.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java index f874137b..50fcfcd3 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java @@ -117,7 +117,12 @@ public class MapCursor implements Cursor { * same position. */ public static int compare(MapCursor cursor1, MapCursor cursor2) { - // two cursors are equally deep + // Checking the state of the cursors + if(!cursor1.isTerminated() && cursor2.isTerminated()) return -1; + else if(cursor1.isTerminated() && !cursor2.isTerminated()) return 1; + else if(cursor1.isTerminated() && cursor2.isTerminated()) return 0; + + // If the state does not determine the order, then compare @nodeIndexStack. Iterator stack1 = cursor1.nodeIndexStack.descendingIterator(); Iterator stack2 = cursor2.nodeIndexStack.descendingIterator(); if (stack1.hasNext()) { @@ -137,6 +142,8 @@ public class MapCursor implements Cursor { // stack 2 has more element, thus stack 2 is deeper return 1; } + + // two cursors are equally deep decide by data index return Integer.compare(cursor1.dataIndex, cursor2.dataIndex); } } -- cgit v1.2.3-54-g00ecf From 80a08f099b220fe7aca45cb6ef30da02be0087ca Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sun, 5 Feb 2023 02:32:27 +0100 Subject: fixup! Performance improvements by replacing hash depth calculation with shifting, improving code quality. + formatting --- .../store/map/internal/VersionedMapImpl.java | 22 ---------------------- 1 file changed, 22 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java index 57256472..674ffc47 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java @@ -131,28 +131,6 @@ public class VersionedMapImpl implements VersionedMap { root = this.store.revert(state); } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + ((root == null) ? 0 : root.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - VersionedMapImpl other = (VersionedMapImpl) obj; - if (root == null) { - return other.root == null; - } else return root.equals(other.root); - } - public String prettyPrint() { if (this.root != null) { StringBuilder s = new StringBuilder(); -- cgit v1.2.3-54-g00ecf From 8403277d0a967f234c46f09cf54185ce3ed01c8b Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sun, 5 Feb 2023 20:05:04 +0100 Subject: Extending map tests with null as default value. --- .../map/benchmarks/ImmutablePutExecutionPlan.java | 2 +- .../store/map/tests/fuzz/CommitFuzzTest.java | 28 ++++++++------ .../map/tests/fuzz/ContentEqualsFuzzTest.java | 27 +++++++------- .../store/map/tests/fuzz/DiffCursorFuzzTest.java | 29 +++++++++------ .../store/map/tests/fuzz/MultiThreadFuzzTest.java | 43 ++++++++++++---------- .../store/map/tests/fuzz/MutableFuzzTest.java | 27 ++++++++------ .../fuzz/MutableImmutableCompareFuzzTest.java | 26 +++++++------ .../store/map/tests/fuzz/RestoreFuzzTest.java | 25 +++++++------ .../store/map/tests/fuzz/SharedStoreFuzzTest.java | 32 ++++++++-------- .../store/map/tests/utils/MapTestEnvironment.java | 9 ++++- 10 files changed, 139 insertions(+), 109 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java b/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java index 756d504e..5484f115 100644 --- a/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java +++ b/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java @@ -35,7 +35,7 @@ public class ImmutablePutExecutionPlan { @Setup(Level.Trial) public void setUpTrial() { random = new Random(); - values = MapTestEnvironment.prepareValues(nValues); + values = MapTestEnvironment.prepareValues(nValues, true); } public VersionedMapImpl createSut() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java index 1f9d022f..c872b9da 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java @@ -19,9 +19,10 @@ import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; class CommitFuzzTest { - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, - boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue); + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, + boolean nullDefault, int commitFrequency, + boolean evilHash) { + String[] values = MapTestEnvironment.prepareValues(maxValue,nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); @@ -64,30 +65,33 @@ class CommitFuzzTest { } } - @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5} " + + "seed={6} evil-hash={7}") @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, + boolean evilHash) { runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - commitFrequency, evilHash); + nullDefault, commitFrequency, evilHash); } static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, + new Object[] { 2, 3 }, new Object[]{false,true}, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, new Object[] { false, true }); } - @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5} " + + "seed={6} evil-hash={7}") @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - commitFrequency, evilHash); + nullDefault, commitFrequency, evilHash); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java index 93ecfec3..a5a68b94 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java @@ -20,9 +20,10 @@ import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; class ContentEqualsFuzzTest { - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, + boolean nullDefault, int commitFrequency, boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue); + String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); Random r = new Random(seed); @@ -80,33 +81,33 @@ class ContentEqualsFuzzTest { MapTestEnvironment.compareTwoMaps(scenario, sut1, sut2); } - @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} " + - "evil-hash={6}") + @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit frequency={5}" + + "seed={6} evil-hash={7}") @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - commitFrequency, evilHash); + nullDefault, commitFrequency, evilHash); } static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, - new Object[]{2, 3}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, + new Object[]{2, 3}, new Object[]{false,true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, new Object[]{false, true}); } - @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} " + - "evil-hash={6}") + @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit frequency={5}" + + "seed={6} evil-hash={7}") @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean defaultNull, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - commitFrequency, evilHash); + defaultNull, commitFrequency, evilHash); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java index e6334224..670b5445 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java @@ -20,17 +20,18 @@ import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; class DiffCursorFuzzTest { - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, + boolean nullDefault, int commitFrequency, boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue); + String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); - iterativeRandomPutsAndCommitsThenDiffcursor(scenario, store, steps, maxKey, values, seed, commitFrequency); + iterativeRandomPutsAndCommitsThenDiffCursor(scenario, store, steps, maxKey, values, seed, commitFrequency); } - private void iterativeRandomPutsAndCommitsThenDiffcursor(String scenario, VersionedMapStore store, - int steps, int maxKey, String[] values, int seed, int commitFrequency) { + private void iterativeRandomPutsAndCommitsThenDiffCursor(String scenario, VersionedMapStore store, + int steps, int maxKey, String[] values, int seed, int commitFrequency) { // 1. build a map with versions Random r = new Random(seed); VersionedMapImpl versioned = (VersionedMapImpl) store.createMap(); @@ -86,29 +87,33 @@ class DiffCursorFuzzTest { } - @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + + "commit frequency={5} seed={6} evil-hash={7}") @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, + void parametrizedFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, - noKeys, noValues, commitFrequency, evilHash); + noKeys, noValues, nullDefault, commitFrequency, evilHash); } static Stream parametrizedFuzz() { return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, + new Object[] { 2, 3 }, new Object[]{false,true}, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, new Object[] { false, true }); } - @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + + "commit frequency={5} seed={6} evil-hash={7}") @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, + void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - commitFrequency, evilHash); + nullDefault, commitFrequency, evilHash); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java index 1ab431a8..d8e1a30f 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java @@ -22,31 +22,32 @@ import tools.refinery.store.map.tests.utils.MapTestEnvironment; class MultiThreadFuzzTest { public static final int noThreads = 32; - - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, - boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue); + + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, + boolean nullDefault, int commitFrequency, + boolean evilHash) { + String[] values = MapTestEnvironment.prepareValues(maxValue,nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); - + // initialize runnables MultiThreadTestRunnable[] runnables = new MultiThreadTestRunnable[noThreads]; for(int i = 0; i errors = new LinkedList<>(); for(int i = 0; i parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[] { 10, 100 }, new Object[] { 1, 2, 3 }, + new Object[] { 2, 3 }, new Object[]{false, true}, new Object[] { 10, 100 }, new Object[] { 1, 2, 3 }, new Object[] { false, true }); } - @ParameterizedTest(name = "Multithread {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Multithread {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit " + + "frequency={5} seed={6} evil-hash={7}") @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, + boolean evilHash) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - commitFrequency, evilHash); + nullDefault, commitFrequency, evilHash); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java index d40c49c4..008258bd 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java @@ -19,8 +19,9 @@ import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; class MutableFuzzTest { - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue); + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, + boolean nullDefault, boolean evilHash) { + String[] values = MapTestEnvironment.prepareValues(maxValue,nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); @@ -60,30 +61,34 @@ class MutableFuzzTest { } } - @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} seed={4} evil-hash={5}") + @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} seed={5} " + + "evil-hash={6}") @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFuzz(int test, int steps, int noKeys, int noValues, int seed, boolean evilHash) { + void parametrizedFuzz(int test, int steps, int noKeys, int noValues, boolean defaultNull, int seed, + boolean evilHash) { runFuzzTest( "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed + "H" + (evilHash ? "Evil" : "Normal"), - seed, steps, noKeys, noValues, evilHash); + seed, steps, noKeys, noValues, defaultNull, evilHash); } static Stream parametrizedFuzz() { return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, - new Object[] { 3, 32, 32 * 32, 32 * 32 * 32 * 32 }, new Object[] { 2, 3 }, new Object[] { 1, 2, 3 }, - new Object[] { false, true }); + new Object[] { 3, 32, 32 * 32, 32 * 32 * 32 * 32 }, new Object[] { 2, 3 }, new Object[] { false, true }, + new Object[] { 1, 2, 3 }, new Object[] { false, true }); } - - @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} seed={4} evil-hash={5}") + + @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} seed={5} " + + "evil-hash={6}") @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int test, int steps, int noKeys, int noValues, int seed, boolean evilHash) { + void parametrizedSlowFuzz(int test, int steps, int noKeys, int noValues, boolean nullDefault, int seed, + boolean evilHash) { runFuzzTest( "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed + "H" + (evilHash ? "Evil" : "Normal"), - seed, steps, noKeys, noValues, evilHash); + seed, steps, noKeys, noValues, nullDefault, evilHash); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java index 410705a2..6e15b8ca 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java @@ -19,9 +19,9 @@ import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; class MutableImmutableCompareFuzzTest { - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, - boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue); + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, + boolean nullDefault, int commitFrequency, boolean evilHash) { + String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); @@ -57,30 +57,32 @@ class MutableImmutableCompareFuzzTest { } } - @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + + "commit frequency={5} seed={6} evil-hash={7}") @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, - noKeys, noValues, commitFrequency, evilHash); + noKeys, noValues, nullDefault, commitFrequency, evilHash); } static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, + new Object[] { 2, 3 }, new Object[]{false,true}, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, new Object[] { false, true }); } - @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + + "commit frequency={5} seed={6} evil-hash={7}") @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, - noKeys, noValues, commitFrequency, evilHash); + noKeys, noValues, nullDefault, commitFrequency, evilHash); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java index 2e29a03f..35a54712 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java @@ -21,9 +21,10 @@ import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; class RestoreFuzzTest { - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, + boolean nullDefault, int commitFrequency, boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue); + String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); @@ -77,30 +78,32 @@ class RestoreFuzzTest { } - @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5}" + + " seed={6} evil-hash={7}") @MethodSource @Timeout(value = 10) @Tag("smoke") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - commitFrequency, evilHash); + nullDefault, commitFrequency, evilHash); } static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, + new Object[] { 2, 3 }, new Object[]{false, true}, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, new Object[] { false, true }); } - @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5}" + + " seed={6} evil-hash={7}") @MethodSource @Tag("smoke") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - commitFrequency, evilHash); + nullDefault, commitFrequency, evilHash); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java index 914a0f63..ac033edb 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java @@ -21,9 +21,9 @@ import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; class SharedStoreFuzzTest { - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, int commitFrequency, - boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue); + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, + boolean nullDefault, int commitFrequency, boolean evilHash) { + String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); List> stores = VersionedMapStoreImpl.createSharedVersionedMapStores(5, chp, values[0]); @@ -39,7 +39,7 @@ class SharedStoreFuzzTest { for(VersionedMapStore store : stores) { versioneds.add((VersionedMapImpl) store.createMap()); } - + List> index2Version = new LinkedList<>(); for(int i = 0; i()); @@ -56,7 +56,7 @@ class SharedStoreFuzzTest { index2Version.get(storeIndex).put(i, version); } MapTestEnvironment.printStatus(scenario, stepIndex, steps, "building"); - } + } } // 2. create a non-versioned and List> reference = new LinkedList<>(); @@ -76,35 +76,37 @@ class SharedStoreFuzzTest { MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference.get(storeIndex), versioneds.get(storeIndex)); } } - MapTestEnvironment.printStatus(scenario, index, steps, "comparison"); + MapTestEnvironment.printStatus(scenario, index, steps, "comparison"); } } - @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit " + + "frequency={4} seed={5} evil-hash={6}") @MethodSource @Timeout(value = 10) @Tag("smoke") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("SharedS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - commitFrequency, evilHash); + nullDefault, commitFrequency, evilHash); } static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, + new Object[] { 2, 3 }, new Object[]{false, true}, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, new Object[] { false, true }); } - @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} commit frequency={4} seed={5} evil-hash={6}") + @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit " + + "frequency={4} seed={5} evil-hash={6}") @MethodSource @Tag("smoke") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + int seed, boolean evilHash) { runFuzzTest("SharedS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - commitFrequency, evilHash); + nullDefault, commitFrequency, evilHash); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java index 2d03ebaf..10ea2796 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java @@ -9,9 +9,14 @@ import java.util.Map.Entry; import static org.junit.jupiter.api.Assertions.*; public class MapTestEnvironment { - public static String[] prepareValues(int maxValue) { + public static String[] prepareValues(int maxValue, boolean nullDefault) { String[] values = new String[maxValue]; - values[0] = "DEFAULT"; + if(nullDefault) { + values[0] = null; + } else { + values[0] = "DEFAULT"; + } + for (int i = 1; i < values.length; i++) { values[i] = "VAL" + i; } -- cgit v1.2.3-54-g00ecf From 01d06bb9d04894029d2be304f6d4dccb757ea9dc Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sun, 5 Feb 2023 20:27:45 +0100 Subject: Formatting fuzz tests + improving code qualitz --- .../store/map/tests/fuzz/CommitFuzzTest.java | 25 +++++----------- .../map/tests/fuzz/ContentEqualsFuzzTest.java | 6 ++-- .../store/map/tests/fuzz/DiffCursorFuzzTest.java | 17 ++++++----- .../store/map/tests/fuzz/MultiThreadFuzzTest.java | 32 ++++++++++---------- .../map/tests/fuzz/MultiThreadTestRunnable.java | 34 +++++++++++----------- .../store/map/tests/fuzz/MutableFuzzTest.java | 26 +++++------------ .../fuzz/MutableImmutableCompareFuzzTest.java | 12 ++++---- .../store/map/tests/fuzz/RestoreFuzzTest.java | 12 ++++---- .../store/map/tests/fuzz/SharedStoreFuzzTest.java | 18 ++++++------ .../store/map/tests/fuzz/utils/FuzzTestUtils.java | 10 +++---- .../map/tests/fuzz/utils/FuzzTestUtilsTest.java | 19 +++++++----- .../store/map/tests/utils/MapTestEnvironment.java | 25 +++++++--------- 12 files changed, 107 insertions(+), 129 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java index c872b9da..cd32337e 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java @@ -22,12 +22,12 @@ class CommitFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue,nullDefault); + String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); + VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); VersionedMapImpl sut = (VersionedMapImpl) store.createMap(); - MapTestEnvironment e = new MapTestEnvironment(sut); + MapTestEnvironment e = new MapTestEnvironment<>(sut); Random r = new Random(seed); @@ -35,24 +35,13 @@ class CommitFuzzTest { } private void iterativeRandomPutsAndCommits(String scenario, int steps, int maxKey, String[] values, - MapTestEnvironment e, Random r, int commitFrequency) { - int stopAt = -1; + MapTestEnvironment e, Random r, int commitFrequency) { for (int i = 0; i < steps; i++) { int index = i + 1; int nextKey = r.nextInt(maxKey); String nextValue = values[r.nextInt(values.length)]; - if (index == stopAt) { - System.out.println("issue!"); - System.out.println("State before:"); - e.printComparison(); - e.sut.prettyPrint(); - System.out.println("Next: put(" + nextKey + "," + nextValue + ")"); - } try { e.put(nextKey, nextValue); - if (index == stopAt) { - e.sut.prettyPrint(); - } e.checkEquivalence(scenario + ":" + index); } catch (Exception exception) { exception.printStackTrace(); @@ -78,9 +67,9 @@ class CommitFuzzTest { } static Stream parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[]{false,true}, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, - new Object[] { false, true }); + return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, + new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, + new Object[]{false, true}); } @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5} " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java index a5a68b94..996bfa03 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java @@ -34,7 +34,7 @@ class ContentEqualsFuzzTest { private void iterativeRandomPutsAndCommitsThenCompare(String scenario, ContinousHashProvider chp, int steps, int maxKey, String[] values, Random r, int commitFrequency) { - VersionedMapStore store1 = new VersionedMapStoreImpl(chp, values[0]); + VersionedMapStore store1 = new VersionedMapStoreImpl<>(chp, values[0]); VersionedMap sut1 = store1.createMap(); // Fill one map @@ -64,7 +64,7 @@ class ContentEqualsFuzzTest { // Randomize the order of the content Collections.shuffle(content, r); - VersionedMapStore store2 = new VersionedMapStoreImpl(chp, values[0]); + VersionedMapStore store2 = new VersionedMapStoreImpl<>(chp, values[0]); VersionedMap sut2 = store2.createMap(); int index2 = 1; for (SimpleEntry entry : content) { @@ -95,7 +95,7 @@ class ContentEqualsFuzzTest { static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, - new Object[]{2, 3}, new Object[]{false,true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, + new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, new Object[]{false, true}); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java index 670b5445..4f7f48b5 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java @@ -22,11 +22,11 @@ import tools.refinery.store.map.tests.utils.MapTestEnvironment; class DiffCursorFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, - boolean evilHash) { + boolean evilHash) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); + VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); iterativeRandomPutsAndCommitsThenDiffCursor(scenario, store, steps, maxKey, values, seed, commitFrequency); } @@ -62,7 +62,7 @@ class DiffCursorFuzzTest { for (int i = 0; i < steps; i++) { int index = i + 1; if (index % diffTravelFrequency == 0) { - // difftravel + // diff-travel long travelToVersion = r2.nextInt(largestCommit + 1); DiffCursor diffCursor = moving.getDiffCursor(travelToVersion); moving.putAll(diffCursor); @@ -94,16 +94,17 @@ class DiffCursorFuzzTest { @Tag("fuzz") void parametrizedFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, - boolean evilHash) { + boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); } static Stream parametrizedFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[]{false,true}, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, - new Object[] { false, true }); + return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, + new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, + new Object[]{false, true}); } + @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + "commit frequency={5} seed={6} evil-hash={7}") @MethodSource @@ -111,7 +112,7 @@ class DiffCursorFuzzTest { @Tag("slow") void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, - boolean evilHash) { + boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java index d8e1a30f..5412d958 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java @@ -26,64 +26,64 @@ class MultiThreadFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue,nullDefault); + String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); + VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); // initialize runnables MultiThreadTestRunnable[] runnables = new MultiThreadTestRunnable[noThreads]; - for(int i = 0; i errors = new LinkedList<>(); - for(int i = 0; i parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[]{false, true}, new Object[] { 10, 100 }, new Object[] { 1, 2, 3 }, - new Object[] { false, true }); + return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, + new Object[]{2, 3}, new Object[]{false, true}, new Object[]{10, 100}, new Object[]{1, 2, 3}, + new Object[]{false, true}); } - @ParameterizedTest(name = "Multithread {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit " + + @ParameterizedTest(name = "MultiThread {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit " + "frequency={5} seed={6} evil-hash={7}") @MethodSource @Tag("fuzz") diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java index f77f9ee5..4415e4e5 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java @@ -13,17 +13,17 @@ import tools.refinery.store.map.internal.VersionedMapImpl; import tools.refinery.store.map.tests.utils.MapTestEnvironment; public class MultiThreadTestRunnable implements Runnable { - String scenario; - VersionedMapStore store; - int steps; - int maxKey; - String[] values; - int seed; - int commitFrequency; - List errors = new LinkedList<>(); - + final String scenario; + final VersionedMapStore store; + final int steps; + final int maxKey; + final String[] values; + final int seed; + final int commitFrequency; + final List errors = new LinkedList<>(); + public MultiThreadTestRunnable(String scenario, VersionedMapStore store, int steps, - int maxKey, String[] values, int seed, int commitFrequency) { + int maxKey, String[] values, int seed, int commitFrequency) { super(); this.scenario = scenario; this.store = store; @@ -38,11 +38,11 @@ public class MultiThreadTestRunnable implements Runnable { AssertionError error = new AssertionError(message); errors.add(error); } - + public List getErrors() { return errors; } - + @Override public void run() { // 1. build a map with versions @@ -66,10 +66,10 @@ public class MultiThreadTestRunnable implements Runnable { } MapTestEnvironment.printStatus(scenario, index, steps, "building"); } - // 2. create a non-versioned + // 2. create a non-versioned VersionedMapImpl reference = (VersionedMapImpl) store.createMap(); r = new Random(seed); - Random r2 = new Random(seed+1); + Random r2 = new Random(seed + 1); for (int i = 0; i < steps; i++) { int index = i + 1; @@ -84,12 +84,12 @@ public class MultiThreadTestRunnable implements Runnable { // go back to an existing state and compare to the reference if (index % (commitFrequency) == 0) { versioned.restore(index2Version.get(i)); - MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference, versioned,errors); - + MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference, versioned, errors); + // go back to a random state (probably created by another thread) List states = new ArrayList<>(store.getStates()); Collections.shuffle(states, r2); - for(Long state : states.subList(0, Math.min(states.size(), 100))) { + for (Long state : states.subList(0, Math.min(states.size(), 100))) { versioned.restore(state); } versioned.restore(index2Version.get(i)); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java index 008258bd..2448268a 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java @@ -21,12 +21,12 @@ import tools.refinery.store.map.tests.utils.MapTestEnvironment; class MutableFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, boolean evilHash) { - String[] values = MapTestEnvironment.prepareValues(maxValue,nullDefault); + String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); + VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); VersionedMapImpl sut = (VersionedMapImpl) store.createMap(); - MapTestEnvironment e = new MapTestEnvironment(sut); + MapTestEnvironment e = new MapTestEnvironment<>(sut); Random r = new Random(seed); @@ -34,24 +34,14 @@ class MutableFuzzTest { } private void iterativeRandomPuts(String scenario, int steps, int maxKey, String[] values, - MapTestEnvironment e, Random r) { - int stopAt = -1; + MapTestEnvironment e, Random r) { for (int i = 0; i < steps; i++) { int index = i + 1; int nextKey = r.nextInt(maxKey); String nextValue = values[r.nextInt(values.length)]; - if (index == stopAt) { - System.out.println("issue!"); - System.out.println("State before:"); - e.printComparison(); - e.sut.prettyPrint(); - System.out.println("Next: put(" + nextKey + "," + nextValue + ")"); - } + try { e.put(nextKey, nextValue); - if (index == stopAt) { - e.sut.prettyPrint(); - } e.checkEquivalence(scenario + ":" + index); } catch (Exception exception) { exception.printStackTrace(); @@ -74,9 +64,9 @@ class MutableFuzzTest { } static Stream parametrizedFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, - new Object[] { 3, 32, 32 * 32, 32 * 32 * 32 * 32 }, new Object[] { 2, 3 }, new Object[] { false, true }, - new Object[] { 1, 2, 3 }, new Object[] { false, true }); + return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, + new Object[]{3, 32, 32 * 32, 32 * 32 * 32 * 32}, new Object[]{2, 3}, new Object[]{false, true}, + new Object[]{1, 2, 3}, new Object[]{false, true}); } @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} seed={5} " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java index 6e15b8ca..07ca4f69 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java @@ -24,7 +24,7 @@ class MutableImmutableCompareFuzzTest { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); + VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); VersionedMapImpl immutable = (VersionedMapImpl) store.createMap(); VersionedMapImpl mutable = (VersionedMapImpl) store.createMap(); @@ -35,8 +35,8 @@ class MutableImmutableCompareFuzzTest { } private void iterativeRandomPutsAndCommitsAndCompare(String scenario, VersionedMapImpl immutable, - VersionedMapImpl mutable, int steps, int maxKey, String[] values, Random r, - int commitFrequency) { + VersionedMapImpl mutable, int steps, int maxKey, String[] values, Random r, + int commitFrequency) { for (int i = 0; i < steps; i++) { int index = i + 1; int nextKey = r.nextInt(maxKey); @@ -69,9 +69,9 @@ class MutableImmutableCompareFuzzTest { } static Stream parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[]{false,true}, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, - new Object[] { false, true }); + return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, + new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, + new Object[]{false, true}); } @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java index 35a54712..e0366381 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java @@ -23,17 +23,17 @@ import tools.refinery.store.map.tests.utils.MapTestEnvironment; class RestoreFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, - boolean evilHash) { + boolean evilHash) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl(chp, values[0]); + VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); iterativeRandomPutsAndCommitsThenRestore(scenario, store, steps, maxKey, values, seed, commitFrequency); } private void iterativeRandomPutsAndCommitsThenRestore(String scenario, VersionedMapStore store, - int steps, int maxKey, String[] values, int seed, int commitFrequency) { + int steps, int maxKey, String[] values, int seed, int commitFrequency) { // 1. build a map with versions Random r = new Random(seed); VersionedMapImpl versioned = (VersionedMapImpl) store.createMap(); @@ -90,9 +90,9 @@ class RestoreFuzzTest { } static Stream parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[]{false, true}, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, - new Object[] { false, true }); + return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, + new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, + new Object[]{false, true}); } @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5}" + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java index ac033edb..a576b1c8 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java @@ -32,22 +32,22 @@ class SharedStoreFuzzTest { } private void iterativeRandomPutsAndCommitsThenRestore(String scenario, List> stores, - int steps, int maxKey, String[] values, int seed, int commitFrequency) { + int steps, int maxKey, String[] values, int seed, int commitFrequency) { // 1. maps with versions Random r = new Random(seed); List> versioneds = new LinkedList<>(); - for(VersionedMapStore store : stores) { + for (VersionedMapStore store : stores) { versioneds.add((VersionedMapImpl) store.createMap()); } List> index2Version = new LinkedList<>(); - for(int i = 0; i()); } for (int i = 0; i < steps; i++) { int stepIndex = i + 1; - for (int storeIndex = 0; storeIndex> reference = new LinkedList<>(); - for(VersionedMapStore store : stores) { + for (VersionedMapStore store : stores) { reference.add((VersionedMapImpl) store.createMap()); } r = new Random(seed); for (int i = 0; i < steps; i++) { int index = i + 1; - for (int storeIndex = 0; storeIndex parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[] { FuzzTestUtils.FAST_STEP_COUNT }, new Object[] { 3, 32, 32 * 32 }, - new Object[] { 2, 3 }, new Object[]{false, true}, new Object[] { 1, 10, 100 }, new Object[] { 1, 2, 3 }, - new Object[] { false, true }); + return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, + new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, + new Object[]{false, true}); } @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java index e75d7f5a..92208e48 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java @@ -51,14 +51,12 @@ public final class FuzzTestUtils { public static Stream permutationWithSize(Object[]... valueOption) { int size = 1; - for (int i = 0; i < valueOption.length; i++) { - size *= valueOption[i].length; + for (Object[] objects : valueOption) { + size *= objects.length; } Object[][] newValueOption = new Object[valueOption.length + 1][]; - newValueOption[0] = new Object[] { size }; - for (int i = 1; i < newValueOption.length; i++) { - newValueOption[i] = valueOption[i - 1]; - } + newValueOption[0] = new Object[]{size}; + System.arraycopy(valueOption, 0, newValueOption, 1, newValueOption.length - 1); return permutation(newValueOption); } } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java index 72f2a46c..8c641205 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtilsTest.java @@ -1,31 +1,36 @@ package tools.refinery.store.map.tests.fuzz.utils; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.provider.Arguments; class FuzzTestUtilsTest { @Test void permutationInternalTest() { - List> res = FuzzTestUtils.permutationInternal(0, new Object[] { 1, 2, 3 }, - new Object[] { 'a', 'b', 'c' }, new Object[] { "alpha", "beta", "gamma", "delta" }); + List> res = FuzzTestUtils.permutationInternal(0, new Object[]{1, 2, 3}, + new Object[]{'a', 'b', 'c'}, new Object[]{"alpha", "beta", "gamma", "delta"}); assertEquals(3 * 3 * 4, res.size()); } @Test void permutationTest1() { - var res = FuzzTestUtils.permutation(new Object[] { 1, 2, 3 }, new Object[] { 'a', 'b', 'c' }, - new Object[] { "alpha", "beta", "gamma", "delta" }); + var res = FuzzTestUtils.permutation(new Object[]{1, 2, 3}, new Object[]{'a', 'b', 'c'}, + new Object[]{"alpha", "beta", "gamma", "delta"}); assertEquals(3 * 3 * 4, res.count()); } @Test void permutationTest2() { - var res = FuzzTestUtils.permutation(new Object[] { 1, 2, 3 }, new Object[] { 'a', 'b', 'c' }, - new Object[] { "alpha", "beta", "gamma", "delta" }); - var arguments = res.findFirst().get().get(); + var res = FuzzTestUtils.permutation(new Object[]{1, 2, 3}, new Object[]{'a', 'b', 'c'}, + new Object[]{"alpha", "beta", "gamma", "delta"}); + Optional first = res.findFirst(); + assertTrue(first.isPresent()); + var arguments = first.get().get(); assertEquals(1, arguments[0]); assertEquals('a', arguments[1]); assertEquals("alpha", arguments[2]); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java index 10ea2796..30f38201 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java @@ -11,7 +11,7 @@ import static org.junit.jupiter.api.Assertions.*; public class MapTestEnvironment { public static String[] prepareValues(int maxValue, boolean nullDefault) { String[] values = new String[maxValue]; - if(nullDefault) { + if (nullDefault) { values[0] = null; } else { values[0] = "DEFAULT"; @@ -26,23 +26,18 @@ public class MapTestEnvironment { public static ContinousHashProvider prepareHashProvider(final boolean evil) { // Use maxPrime = 2147483629 - ContinousHashProvider chp = new ContinousHashProvider() { - - @Override - public int getHash(Integer key, int index) { - if (evil && index < 15 && index < key / 3) { - return 7; - } - int result = 1; - final int prime = 31; + return (key, index) -> { + if (evil && index < 15 && index < key / 3) { + return 7; + } + int result = 1; + final int prime = 31; - result = prime * result + key; - result = prime * result + index; + result = prime * result + key; + result = prime * result + index; - return result; - } + return result; }; - return chp; } public static void printStatus(String scenario, int actual, int max, String stepName) { -- cgit v1.2.3-54-g00ecf From 3b7bb0e09a292df61892613fda8ff1b17a999369 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sun, 5 Feb 2023 20:34:15 +0100 Subject: Fixing warning caused by an "unused parameter" which is used by an annotation --- .../test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java | 4 ++-- .../tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java | 4 ++-- .../java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java | 4 ++-- .../java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java | 4 ++-- .../java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java | 4 ++-- .../store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java | 4 ++-- .../java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java | 4 ++-- .../java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java | 4 ++-- 8 files changed, 16 insertions(+), 16 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java index cd32337e..9e3f636b 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java @@ -59,7 +59,7 @@ class CommitFuzzTest { @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, @@ -77,7 +77,7 @@ class CommitFuzzTest { @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java index 996bfa03..71cb3d11 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java @@ -86,7 +86,7 @@ class ContentEqualsFuzzTest { @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); @@ -104,7 +104,7 @@ class ContentEqualsFuzzTest { @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean defaultNull, int commitFrequency, + void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, defaultNull, commitFrequency, evilHash); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java index 4f7f48b5..8a5576aa 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java @@ -92,7 +92,7 @@ class DiffCursorFuzzTest { @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, @@ -110,7 +110,7 @@ class DiffCursorFuzzTest { @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java index 5412d958..5d5f3ce3 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java @@ -71,7 +71,7 @@ class MultiThreadFuzzTest { @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean defaultNull, int commitFrequency, + void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("MultiThreadS" + steps + "K" + noKeys + "V" + noValues + defaultNull + "CF" + commitFrequency + "s" + seed, seed, steps, noKeys, noValues, defaultNull, commitFrequency, evilHash); @@ -88,7 +88,7 @@ class MultiThreadFuzzTest { @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, + void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java index 2448268a..2d65ba0c 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java @@ -56,7 +56,7 @@ class MutableFuzzTest { @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFuzz(int test, int steps, int noKeys, int noValues, boolean defaultNull, int seed, + void parametrizedFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, int seed, boolean evilHash) { runFuzzTest( "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed + "H" + (evilHash ? "Evil" : "Normal"), @@ -74,7 +74,7 @@ class MutableFuzzTest { @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int test, int steps, int noKeys, int noValues, boolean nullDefault, int seed, + void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int seed, boolean evilHash) { runFuzzTest( "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed + "H" + (evilHash ? "Evil" : "Normal"), diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java index 07ca4f69..da8a43c2 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java @@ -62,7 +62,7 @@ class MutableImmutableCompareFuzzTest { @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); @@ -79,7 +79,7 @@ class MutableImmutableCompareFuzzTest { @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java index e0366381..bd03d1e9 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java @@ -83,7 +83,7 @@ class RestoreFuzzTest { @MethodSource @Timeout(value = 10) @Tag("smoke") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); @@ -100,7 +100,7 @@ class RestoreFuzzTest { @MethodSource @Tag("smoke") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java index a576b1c8..0fc9cd38 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java @@ -86,7 +86,7 @@ class SharedStoreFuzzTest { @MethodSource @Timeout(value = 10) @Tag("smoke") - void parametrizedFastFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("SharedS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); @@ -103,7 +103,7 @@ class SharedStoreFuzzTest { @MethodSource @Tag("smoke") @Tag("slow") - void parametrizedSlowFuzz(int tests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, + void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean evilHash) { runFuzzTest("SharedS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); -- cgit v1.2.3-54-g00ecf From a36a8eb9ad499eb0f50204e462e9bc1c45544294 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sun, 5 Feb 2023 21:35:28 +0100 Subject: Delta store commit --- .../store/map/VersionedMapStoreDeltaImpl.java | 90 ++++++++++++ .../store/map/internal/DeltaDiffCursor.java | 127 +++++++++++++++++ .../store/map/internal/IteratorAsCursor.java | 62 ++++++++ .../refinery/store/map/internal/MapDelta.java | 15 ++ .../store/map/internal/MapTransaction.java | 34 +++++ .../map/internal/UncommittedDeltaArrayStore.java | 31 ++++ .../map/internal/UncommittedDeltaMapStore.java | 47 +++++++ .../store/map/internal/UncommittedDeltaStore.java | 10 ++ .../store/map/internal/VersionedMapDeltaImpl.java | 156 +++++++++++++++++++++ 9 files changed, 572 insertions(+) create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java new file mode 100644 index 00000000..2bd758e2 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java @@ -0,0 +1,90 @@ +package tools.refinery.store.map; + +import java.util.*; + +import tools.refinery.store.map.internal.DeltaDiffCursor; +import tools.refinery.store.map.internal.MapDelta; +import tools.refinery.store.map.internal.MapTransaction; +import tools.refinery.store.map.internal.VersionedMapDeltaImpl; + +public class VersionedMapStoreDeltaImpl implements VersionedMapStore{ + // Static data + protected final V defaultValue; + + // Dynamic data + protected final Map> states = new HashMap<>(); + protected long nextID = 0; + + public VersionedMapStoreDeltaImpl(V defaultValue) { + super(); + this.defaultValue = defaultValue; + } + + @Override + public VersionedMap createMap() { + return new VersionedMapDeltaImpl<>(this, defaultValue); + } + + @Override + public VersionedMap createMap(long state) { + VersionedMapDeltaImpl result = new VersionedMapDeltaImpl<>(this, defaultValue); + result.restore(state); + return result; + } + + public synchronized MapTransaction appendTransaction(MapDelta[] deltas, MapTransaction previous, long[] versionContainer) { + long version = nextID++; + versionContainer[0] = version; + if(deltas == null) { + states.put(version, previous); + return previous; + } else { + MapTransaction transaction = new MapTransaction<>(deltas, version, previous); + states.put(version, transaction); + return transaction; + } + } + + private synchronized MapTransaction getState(long state) { + return states.get(state); + } + + public void getPath(long to, List[]> forwardTransactions) { + MapTransaction toTransaction = getState(to); + while(toTransaction != null) { + forwardTransactions.add(toTransaction.deltas()); + toTransaction = toTransaction.parent(); + } + } + + public void getPath(long from, long to, + List[]> backwardTransactions, + List[]> forwardTransactions) + { + MapTransaction fromTransaction = getState(from); + MapTransaction toTransaction = getState(to); + while(fromTransaction != toTransaction) { + if(fromTransaction == null || fromTransaction.version() < toTransaction.version()) { + forwardTransactions.add(toTransaction.deltas()); + toTransaction = toTransaction.parent(); + } else { + backwardTransactions.add(fromTransaction.deltas()); + fromTransaction = fromTransaction.parent(); + } + } + } + + + @Override + public synchronized Set getStates() { + return states.keySet(); + } + + @Override + public DiffCursor getDiffCursor(long fromState, long toState) { + List[]> backwardTransactions = new ArrayList<>(); + List[]> forwardTransactions = new ArrayList<>(); + getPath(fromState, toState, backwardTransactions, forwardTransactions); + return new DeltaDiffCursor<>(backwardTransactions, forwardTransactions); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java new file mode 100644 index 00000000..75180bf9 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java @@ -0,0 +1,127 @@ +package tools.refinery.store.map.internal; + +import tools.refinery.store.map.AnyVersionedMap; +import tools.refinery.store.map.DiffCursor; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class DeltaDiffCursor implements DiffCursor { + final List[]> backwardTransactions; + final List[]> forwardTransactions; + + /** + * Denotes the direction of traversal. False means backwards, true means + * forward. + */ + boolean direction; + int listIndex; + int arrayIndex; + + public DeltaDiffCursor(List[]> backwardTransactions, List[]> forwardTransactions) { + this.backwardTransactions = backwardTransactions; + this.forwardTransactions = forwardTransactions; + + if (!backwardTransactions.isEmpty()) { + direction = false; + listIndex = 0; + arrayIndex = backwardTransactions.get(listIndex).length - 1; + } else if (!forwardTransactions.isEmpty()) { + direction = true; + listIndex = forwardTransactions.size() - 1; + arrayIndex = 0; + } else { + direction = true; + listIndex = -1; + } + } + + protected MapDelta getCurrentDelta() { + final List[]> list; + if (!direction) { + list = this.backwardTransactions; + } else { + list = this.forwardTransactions; + } + return list.get(listIndex)[arrayIndex]; + } + + @Override + public K getKey() { + return getCurrentDelta().getKey(); + } + + @Override + public V getValue() { + return getToValue(); + } + + @Override + public boolean isTerminated() { + return this.direction && listIndex == -1; + } + + @Override + public boolean move() { + if (isTerminated()) { + return false; + } else { + if (this.direction) { + if (arrayIndex+1 < forwardTransactions.get(listIndex).length) { + arrayIndex++; + return true; + } else { + if (listIndex-1 >= 0) { + listIndex--; + return true; + } else { + listIndex = -1; + return false; + } + } + } else { + if (arrayIndex > 0) { + arrayIndex--; + return true; + } else { + if (listIndex+1 < backwardTransactions.size()) { + listIndex++; + this.arrayIndex = backwardTransactions.get(listIndex).length - 1; + return true; + } else { + this.direction = true; + if (!this.forwardTransactions.isEmpty()) { + listIndex = forwardTransactions.size() - 1; + arrayIndex = 0; + return true; + } else { + listIndex = -1; + return false; + } + } + } + } + } + } + + @Override + public boolean isDirty() { + return false; + } + + @Override + public Set getDependingMaps() { + return Collections.emptySet(); + } + + @Override + public V getFromValue() { + return getCurrentDelta().getOldValue(); + } + + @Override + public V getToValue() { + return getCurrentDelta().getNewValue(); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java new file mode 100644 index 00000000..4a8e9709 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java @@ -0,0 +1,62 @@ +package tools.refinery.store.map.internal; + +import java.util.*; +import java.util.Map.Entry; + +import tools.refinery.store.map.AnyVersionedMap; +import tools.refinery.store.map.Cursor; +import tools.refinery.store.map.VersionedMap; + +public class IteratorAsCursor implements Cursor { + final Iterator> iterator; + final VersionedMap source; + + private boolean terminated; + private K key; + private V value; + + public IteratorAsCursor(VersionedMap source, Map current) { + this.iterator = current.entrySet().iterator(); + this.source = source; + move(); + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public boolean isTerminated() { + return terminated; + } + + @Override + public boolean move() { + terminated = iterator.hasNext(); + if (terminated) { + this.key = null; + this.value = null; + } else { + Entry next = iterator.next(); + this.key = next.getKey(); + this.value = next.getValue(); + } + return !terminated; + } + + @Override + public boolean isDirty() { + return false; + } + + @Override + public Set getDependingMaps() { + return Set.of(this.source); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java new file mode 100644 index 00000000..86e9fe62 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java @@ -0,0 +1,15 @@ +package tools.refinery.store.map.internal; + +public record MapDelta(K key, V oldValue, V newValue) { + public K getKey() { + return key; + } + + public V getOldValue() { + return oldValue; + } + + public V getNewValue() { + return newValue; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java new file mode 100644 index 00000000..5996048e --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java @@ -0,0 +1,34 @@ +package tools.refinery.store.map.internal; + +import java.util.Arrays; +import java.util.Objects; + +public record MapTransaction(MapDelta[] deltas, long version, MapTransaction parent) { + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(deltas); + result = prime * result + Objects.hash(parent, version); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + @SuppressWarnings("unchecked") + MapTransaction other = (MapTransaction) obj; + return Arrays.equals(deltas, other.deltas) && Objects.equals(parent, other.parent) && version == other.version; + } + + @Override + public String toString() { + return "MapTransaction " + version + " " + Arrays.toString(deltas); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java new file mode 100644 index 00000000..3b3f94ae --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java @@ -0,0 +1,31 @@ +package tools.refinery.store.map.internal; + +import java.util.ArrayList; +import java.util.List; + +public class UncommittedDeltaArrayStore implements UncommittedDeltaStore { + final List> uncommittedOldValues = new ArrayList<>(); + + @Override + public void processChange(K key, V oldValue, V newValue) { + uncommittedOldValues.add(new MapDelta<>(key, oldValue, newValue)); + } + + @Override + public MapDelta[] extractDeltas() { + if (uncommittedOldValues.isEmpty()) { + return null; + } else { + @SuppressWarnings("unchecked") + MapDelta[] result = uncommittedOldValues.toArray(new MapDelta[0]); + return result; + } + } + + @Override + public MapDelta[] extractAndDeleteDeltas() { + MapDelta[] res = extractDeltas(); + this.uncommittedOldValues.clear(); + return res; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java new file mode 100644 index 00000000..73df5080 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java @@ -0,0 +1,47 @@ +package tools.refinery.store.map.internal; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.store.map.VersionedMap; + +public class UncommittedDeltaMapStore implements UncommittedDeltaStore { + final VersionedMap source; + final Map uncommittedOldValues = new HashMap<>(); + + public UncommittedDeltaMapStore(VersionedMap source) { + this.source = source; + } + + @Override + public void processChange(K key, V oldValue, V newValue) { + this.uncommittedOldValues.putIfAbsent(key, oldValue); + } + + @Override + public MapDelta[] extractDeltas() { + if (uncommittedOldValues.isEmpty()) { + return null; + } else { + @SuppressWarnings("unchecked") + MapDelta[] deltas = new MapDelta[uncommittedOldValues.size()]; + int i = 0; + for (Entry entry : uncommittedOldValues.entrySet()) { + final K key = entry.getKey(); + final V oldValue = entry.getValue(); + final V newValue = source.get(key); + deltas[i] = new MapDelta<>(key, oldValue, newValue); + } + + return deltas; + } + } + + @Override + public MapDelta[] extractAndDeleteDeltas() { + MapDelta[] res = extractDeltas(); + this.uncommittedOldValues.clear(); + return res; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java new file mode 100644 index 00000000..37e5817c --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java @@ -0,0 +1,10 @@ +package tools.refinery.store.map.internal; + +public interface UncommittedDeltaStore { + void processChange(K key, V oldValue, V newValue); + + MapDelta[] extractDeltas(); + + MapDelta[] extractAndDeleteDeltas(); + +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java new file mode 100644 index 00000000..deedf134 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java @@ -0,0 +1,156 @@ +package tools.refinery.store.map.internal; + +import java.util.*; + +import tools.refinery.store.map.*; + +public class VersionedMapDeltaImpl implements VersionedMap { + protected final VersionedMapStoreDeltaImpl store; + + final Map current; + + final UncommittedDeltaStore uncommittedStore; + MapTransaction previous; + + protected final V defaultValue; + + public VersionedMapDeltaImpl(VersionedMapStoreDeltaImpl store, V defaultValue) { + this.store = store; + this.defaultValue = defaultValue; + + current = new HashMap<>(); + uncommittedStore = new UncommittedDeltaArrayStore<>(); + } + + @Override + public long commit() { + MapDelta[] deltas = uncommittedStore.extractAndDeleteDeltas(); + long[] versionContainer = new long[1]; + this.previous = this.store.appendTransaction(deltas, previous, versionContainer); + return versionContainer[0]; + } + + @Override + public void restore(long state) { + // 1. restore uncommitted states + MapDelta[] uncommitted = this.uncommittedStore.extractAndDeleteDeltas(); + if (uncommitted != null) { + backward(uncommitted); + } + + // 2. get common ancestor + List[]> forward = new ArrayList<>(); + if (this.previous == null) { + this.store.getPath(state, forward); + this.forward(forward); + } else { + List[]> backward = new ArrayList<>(); + this.store.getPath(this.previous.version(), state, backward, forward); + this.backward(backward); + this.forward(forward); + } + } + + protected void forward(List[]> changes) { + for (int i = changes.size() - 1; i >= 0; i--) { + forward(changes.get(i)); + } + } + + protected void backward(List[]> changes) { + for (int i = 0; i < changes.size(); i++) { + backward(changes.get(i)); + } + } + + protected void forward(MapDelta[] changes) { + for (int i = 0; i < changes.length; i++) { + final MapDelta change = changes[i]; + current.put(change.getKey(), change.getNewValue()); + } + } + + protected void backward(MapDelta[] changes) { + for (int i = changes.length - 1; i >= 0; i--) { + final MapDelta change = changes[i]; + current.put(change.getKey(), change.getOldValue()); + } + } + + @Override + public V get(K key) { + return current.getOrDefault(key, defaultValue); + } + + @Override + public Cursor getAll() { + return new IteratorAsCursor<>(this, current); + } + + @Override + public V put(K key, V value) { + if (value == defaultValue) { + V res = current.remove(key); + if (res == null) { + // no changes + return defaultValue; + } else { + uncommittedStore.processChange(key, res, value); + return res; + } + } else { + V oldValue = current.put(key, value); + uncommittedStore.processChange(key, oldValue, value); + return oldValue; + } + } + + @Override + public void putAll(Cursor cursor) { + throw new UnsupportedOperationException(); + + } + + @Override + public long getSize() { + return current.size(); + } + + @Override + public DiffCursor getDiffCursor(long state) { + MapDelta[] backward = this.uncommittedStore.extractDeltas(); + List[]> backwardTransactions = new ArrayList<>(); + List[]> forwardTransactions = new ArrayList<>(); + + if (backward != null) { + backwardTransactions.add(backward); + } + + if (this.previous != null) { + store.getPath(this.previous.version(), state, backwardTransactions, forwardTransactions); + } else { + store.getPath(state, forwardTransactions); + } + + return new DeltaDiffCursor<>(backwardTransactions, forwardTransactions); + } + + @Override + public int contentHashCode(ContentHashCode mode) { + return this.current.hashCode(); + } + + @Override + public boolean contentEquals(AnyVersionedMap other) { + if (other instanceof VersionedMapDeltaImpl versioned) { + if (versioned == this) { + return true; + } else { + return Objects.equals(this.defaultValue, versioned.defaultValue) && + Objects.equals(this.current, versioned.current); + } + } else { + throw new UnsupportedOperationException("Comparing different map implementations is ineffective."); + } + } +} -- cgit v1.2.3-54-g00ecf From 07dfaba3102075d7669927b2b189dffe95044e94 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Fri, 10 Feb 2023 01:41:04 +0100 Subject: VersionedMapStoreBuilder for delta and state based stores --- .../store/map/VersionedMapStoreBuilder.java | 81 ++++++++++++++++++++++ .../store/map/VersionedMapStoreDeltaImpl.java | 41 ++++++----- .../store/map/internal/VersionedMapDeltaImpl.java | 11 +-- 3 files changed, 108 insertions(+), 25 deletions(-) create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java new file mode 100644 index 00000000..7e413a86 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java @@ -0,0 +1,81 @@ +package tools.refinery.store.map; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class VersionedMapStoreBuilder { + public enum StoreStrategy { + STATE, DELTA + } + + public enum DeltaStorageStrategy { + LIST, SET + } + + public enum StateStorageStrategy { + NO_NODE_CACHE, SHARED_NODE_CACHE, SHARED_NODE_CACHE_IN_GROUP + } + + protected Optional defaultValue = Optional.empty(); + protected StoreStrategy strategy = StoreStrategy.DELTA; + protected Boolean stateBasedImmutableWhenCommitting = false; + protected StateStorageStrategy stateBasedNodeSharingStrategy = StateStorageStrategy.SHARED_NODE_CACHE_IN_GROUP; + protected Optional> hashProvider = Optional.empty(); + protected DeltaStorageStrategy deltaStorageStrategy = DeltaStorageStrategy.LIST; + + public void setDefaultValue(V defaultValue) { + this.defaultValue = Optional.of(defaultValue); + } + + public void setStrategy(StoreStrategy strategy) { + this.strategy = strategy; + } + + public void setHashProvider(ContinousHashProvider hashProvider) { + this.hashProvider = Optional.of(hashProvider); + } + + public void setStateBasedImmutableWhenCommitting(boolean toImmutableWhenCommitting) { + this.stateBasedImmutableWhenCommitting = toImmutableWhenCommitting; + } + + public void setStateBasedNodeSharingStrategy(StateStorageStrategy strategy) { + this.stateBasedNodeSharingStrategy = strategy; + } + + public VersionedMapStore buildOne() { + return switch (strategy) { + case DELTA -> new VersionedMapStoreDeltaImpl<>( + this.deltaStorageStrategy == DeltaStorageStrategy.SET, + this.defaultValue.orElseThrow()); + case STATE -> new VersionedMapStoreImpl<>( + this.hashProvider.orElseThrow(), + this.defaultValue.orElseThrow(), + new VersionedMapStoreConfiguration( + this.stateBasedImmutableWhenCommitting, + this.stateBasedNodeSharingStrategy != StateStorageStrategy.NO_NODE_CACHE, + this.stateBasedNodeSharingStrategy == StateStorageStrategy.SHARED_NODE_CACHE_IN_GROUP)); + }; + } + + public List> buildGroup(int amount) { + if (this.strategy == StoreStrategy.STATE && + this.stateBasedNodeSharingStrategy == StateStorageStrategy.SHARED_NODE_CACHE_IN_GROUP) { + return VersionedMapStoreImpl.createSharedVersionedMapStores( + amount, + this.hashProvider.orElseThrow(), + this.defaultValue.orElseThrow(), + new VersionedMapStoreConfiguration( + this.stateBasedImmutableWhenCommitting, + true, + true)); + } else { + List> result = new ArrayList<>(amount); + for (int i = 0; i < amount; i++) { + result.add(buildOne()); + } + return result; + } + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java index 2bd758e2..98dec2bb 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java @@ -2,32 +2,32 @@ package tools.refinery.store.map; import java.util.*; -import tools.refinery.store.map.internal.DeltaDiffCursor; -import tools.refinery.store.map.internal.MapDelta; -import tools.refinery.store.map.internal.MapTransaction; -import tools.refinery.store.map.internal.VersionedMapDeltaImpl; +import tools.refinery.store.map.internal.*; + +public class VersionedMapStoreDeltaImpl implements VersionedMapStore { + // Configuration + protected final boolean summarizeChanges; -public class VersionedMapStoreDeltaImpl implements VersionedMapStore{ // Static data protected final V defaultValue; // Dynamic data - protected final Map> states = new HashMap<>(); + protected final Map> states = new HashMap<>(); protected long nextID = 0; - public VersionedMapStoreDeltaImpl(V defaultValue) { - super(); + public VersionedMapStoreDeltaImpl(boolean summarizeChanges, V defaultValue) { + this.summarizeChanges = summarizeChanges; this.defaultValue = defaultValue; } @Override public VersionedMap createMap() { - return new VersionedMapDeltaImpl<>(this, defaultValue); + return new VersionedMapDeltaImpl<>(this, this.summarizeChanges, this.defaultValue); } @Override public VersionedMap createMap(long state) { - VersionedMapDeltaImpl result = new VersionedMapDeltaImpl<>(this, defaultValue); + VersionedMapDeltaImpl result = new VersionedMapDeltaImpl<>(this, this.summarizeChanges, this.defaultValue); result.restore(state); return result; } @@ -35,7 +35,7 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore public synchronized MapTransaction appendTransaction(MapDelta[] deltas, MapTransaction previous, long[] versionContainer) { long version = nextID++; versionContainer[0] = version; - if(deltas == null) { + if (deltas == null) { states.put(version, previous); return previous; } else { @@ -45,26 +45,25 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore } } - private synchronized MapTransaction getState(long state) { + private synchronized MapTransaction getState(long state) { return states.get(state); } public void getPath(long to, List[]> forwardTransactions) { - MapTransaction toTransaction = getState(to); - while(toTransaction != null) { + MapTransaction toTransaction = getState(to); + while (toTransaction != null) { forwardTransactions.add(toTransaction.deltas()); toTransaction = toTransaction.parent(); } } public void getPath(long from, long to, - List[]> backwardTransactions, - List[]> forwardTransactions) - { - MapTransaction fromTransaction = getState(from); - MapTransaction toTransaction = getState(to); - while(fromTransaction != toTransaction) { - if(fromTransaction == null || fromTransaction.version() < toTransaction.version()) { + List[]> backwardTransactions, + List[]> forwardTransactions) { + MapTransaction fromTransaction = getState(from); + MapTransaction toTransaction = getState(to); + while (fromTransaction != toTransaction) { + if (fromTransaction == null || fromTransaction.version() < toTransaction.version()) { forwardTransactions.add(toTransaction.deltas()); toTransaction = toTransaction.parent(); } else { diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java index deedf134..6f2996e1 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java @@ -14,12 +14,16 @@ public class VersionedMapDeltaImpl implements VersionedMap { protected final V defaultValue; - public VersionedMapDeltaImpl(VersionedMapStoreDeltaImpl store, V defaultValue) { + public VersionedMapDeltaImpl(VersionedMapStoreDeltaImpl store, boolean summarizeChanges, V defaultValue) { this.store = store; this.defaultValue = defaultValue; current = new HashMap<>(); - uncommittedStore = new UncommittedDeltaArrayStore<>(); + if(summarizeChanges) { + this.uncommittedStore = new UncommittedDeltaMapStore<>(this); + } else { + this.uncommittedStore = new UncommittedDeltaArrayStore<>(); + } } @Override @@ -146,8 +150,7 @@ public class VersionedMapDeltaImpl implements VersionedMap { if (versioned == this) { return true; } else { - return Objects.equals(this.defaultValue, versioned.defaultValue) && - Objects.equals(this.current, versioned.current); + return Objects.equals(this.defaultValue, versioned.defaultValue) && Objects.equals(this.current, versioned.current); } } else { throw new UnsupportedOperationException("Comparing different map implementations is ineffective."); -- cgit v1.2.3-54-g00ecf From 55c8acd527bf0b5608e15587be9a9bfad3dc3c6b Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Fri, 10 Feb 2023 11:49:47 +0100 Subject: Moved test parametrization to FuzzTestCollections.java --- .../tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java | 8 +++++--- .../refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java | 7 +++---- .../refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java | 12 +++++------- .../refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java | 7 ++++--- .../tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java | 6 +++--- .../map/tests/fuzz/MutableImmutableCompareFuzzTest.java | 6 +++--- .../tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java | 6 +++--- .../refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java | 7 ++++--- .../store/map/tests/fuzz/utils/FuzzTestCollections.java | 11 +++++++++++ 9 files changed, 41 insertions(+), 29 deletions(-) create mode 100644 subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java (limited to 'subprojects') diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java index 9e3f636b..b61152f5 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java @@ -18,7 +18,10 @@ import tools.refinery.store.map.internal.VersionedMapImpl; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; + class CommitFuzzTest { + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, boolean evilHash) { @@ -67,9 +70,8 @@ class CommitFuzzTest { } static Stream parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, - new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, - new Object[]{false, true}); + return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, + commitFrequencyOptions, randomSeedOptions, evilHashOptions); } @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5} " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java index 71cb3d11..c17b0a95 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java @@ -18,6 +18,7 @@ import java.util.Random; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.*; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; class ContentEqualsFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, @@ -93,10 +94,8 @@ class ContentEqualsFuzzTest { } static Stream parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, - 32 * 32}, - new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, - new Object[]{false, true}); + return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, + commitFrequencyOptions, randomSeedOptions, evilHashOptions); } @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit frequency={5}" + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java index 8a5576aa..90aa8e01 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java @@ -1,6 +1,7 @@ package tools.refinery.store.map.tests.fuzz; import static org.junit.jupiter.api.Assertions.fail; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; import java.util.Random; import java.util.stream.Stream; @@ -93,16 +94,14 @@ class DiffCursorFuzzTest { @Timeout(value = 10) @Tag("fuzz") void parametrizedFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, - boolean evilHash) { + int seed, boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); } static Stream parametrizedFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, - new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, - new Object[]{false, true}); + return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, + commitFrequencyOptions, randomSeedOptions, evilHashOptions); } @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + @@ -111,8 +110,7 @@ class DiffCursorFuzzTest { @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, - boolean evilHash) { + int seed, boolean evilHash) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, evilHash); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java index 5d5f3ce3..9325a938 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java @@ -2,6 +2,7 @@ package tools.refinery.store.map.tests.fuzz; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.fail; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; import java.util.Collections; import java.util.LinkedList; @@ -78,9 +79,9 @@ class MultiThreadFuzzTest { } static Stream parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, - new Object[]{2, 3}, new Object[]{false, true}, new Object[]{10, 100}, new Object[]{1, 2, 3}, - new Object[]{false, true}); + return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, + new Object[]{10, 100}, randomSeedOptions, + evilHashOptions); } @ParameterizedTest(name = "MultiThread {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java index 2d65ba0c..ab37ef83 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java @@ -1,6 +1,7 @@ package tools.refinery.store.map.tests.fuzz; import static org.junit.jupiter.api.Assertions.fail; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; import java.util.Random; import java.util.stream.Stream; @@ -64,9 +65,8 @@ class MutableFuzzTest { } static Stream parametrizedFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, - new Object[]{3, 32, 32 * 32, 32 * 32 * 32 * 32}, new Object[]{2, 3}, new Object[]{false, true}, - new Object[]{1, 2, 3}, new Object[]{false, true}); + return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, + randomSeedOptions, evilHashOptions); } @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} seed={5} " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java index da8a43c2..b58b06f9 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java @@ -1,6 +1,7 @@ package tools.refinery.store.map.tests.fuzz; import static org.junit.jupiter.api.Assertions.fail; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; import java.util.Random; import java.util.stream.Stream; @@ -69,9 +70,8 @@ class MutableImmutableCompareFuzzTest { } static Stream parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, - new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, - new Object[]{false, true}); + return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, + commitFrequencyOptions, randomSeedOptions, evilHashOptions); } @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java index bd03d1e9..77b26c41 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java @@ -1,6 +1,7 @@ package tools.refinery.store.map.tests.fuzz; import static org.junit.jupiter.api.Assertions.fail; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; import java.util.HashMap; import java.util.Map; @@ -90,9 +91,8 @@ class RestoreFuzzTest { } static Stream parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, - new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, - new Object[]{false, true}); + return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, + commitFrequencyOptions, randomSeedOptions, evilHashOptions); } @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5}" + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java index 0fc9cd38..4462c55b 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java @@ -20,6 +20,8 @@ import tools.refinery.store.map.internal.VersionedMapImpl; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; + class SharedStoreFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, boolean evilHash) { @@ -93,9 +95,8 @@ class SharedStoreFuzzTest { } static Stream parametrizedFastFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[]{FuzzTestUtils.FAST_STEP_COUNT}, new Object[]{3, 32, 32 * 32}, - new Object[]{2, 3}, new Object[]{false, true}, new Object[]{1, 10, 100}, new Object[]{1, 2, 3}, - new Object[]{false, true}); + return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, + commitFrequencyOptions, randomSeedOptions, evilHashOptions); } @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java new file mode 100644 index 00000000..add4ca5d --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java @@ -0,0 +1,11 @@ +package tools.refinery.store.map.tests.fuzz.utils; + +public final class FuzzTestCollections { + public static final Object[] stepCounts = {FuzzTestUtils.FAST_STEP_COUNT}; + public static final Object[] keyCounts = {3, 32, 32 * 32}; + public static final Object[] valueCounts = {2, 3}; + public static final Object[] nullDefaultOptions = {false, true}; + public static final Object[] commitFrequencyOptions = {1, 10, 100}; + public static final Object[] randomSeedOptions = {1, 2, 3}; + public static final Object[] evilHashOptions = {false, true}; +} -- cgit v1.2.3-54-g00ecf From 1543e42dd5b63af6f4228ec545d4f2666ff73274 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Mon, 13 Feb 2023 01:15:44 +0100 Subject: VersionedMapStoreBuilder returns builder state. --- .../store/map/VersionedMapStoreBuilder.java | 85 +++++++++++++++++----- 1 file changed, 67 insertions(+), 18 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java index 7e413a86..1a9aa0b3 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java @@ -2,7 +2,6 @@ package tools.refinery.store.map; import java.util.ArrayList; import java.util.List; -import java.util.Optional; public class VersionedMapStoreBuilder { public enum StoreStrategy { @@ -17,41 +16,76 @@ public class VersionedMapStoreBuilder { NO_NODE_CACHE, SHARED_NODE_CACHE, SHARED_NODE_CACHE_IN_GROUP } - protected Optional defaultValue = Optional.empty(); + public static VersionedMapStoreBuilder builder() { + return new VersionedMapStoreBuilder<>(); + } + protected VersionedMapStoreBuilder() { + } + protected VersionedMapStoreBuilder(VersionedMapStoreBuilder other) { + this.defaultValue = other.defaultValue; + this.defaultSet = other.defaultSet; + this.strategy = other.strategy; + this.stateBasedImmutableWhenCommitting = other.stateBasedImmutableWhenCommitting; + this.stateBasedNodeSharingStrategy = other.stateBasedNodeSharingStrategy; + this.hashProvider = other.hashProvider; + this.deltaStorageStrategy = other.deltaStorageStrategy; + } + protected boolean defaultSet = false; + protected V defaultValue = null; protected StoreStrategy strategy = StoreStrategy.DELTA; protected Boolean stateBasedImmutableWhenCommitting = false; protected StateStorageStrategy stateBasedNodeSharingStrategy = StateStorageStrategy.SHARED_NODE_CACHE_IN_GROUP; - protected Optional> hashProvider = Optional.empty(); + protected ContinousHashProvider hashProvider = null; protected DeltaStorageStrategy deltaStorageStrategy = DeltaStorageStrategy.LIST; - public void setDefaultValue(V defaultValue) { - this.defaultValue = Optional.of(defaultValue); + public VersionedMapStoreBuilder setDefaultValue(V defaultValue) { + var result = new VersionedMapStoreBuilder<>(this); + result.defaultValue = defaultValue; + result.defaultSet = true; + return result; + } + + public VersionedMapStoreBuilder setStrategy(StoreStrategy strategy) { + var result = new VersionedMapStoreBuilder<>(this); + result.strategy = strategy; + return result; } - public void setStrategy(StoreStrategy strategy) { - this.strategy = strategy; + public VersionedMapStoreBuilder setHashProvider(ContinousHashProvider hashProvider) { + var result = new VersionedMapStoreBuilder<>(this); + result.hashProvider = hashProvider; + return result; } - public void setHashProvider(ContinousHashProvider hashProvider) { - this.hashProvider = Optional.of(hashProvider); + public VersionedMapStoreBuilder setStateBasedImmutableWhenCommitting(boolean toImmutableWhenCommitting) { + var result = new VersionedMapStoreBuilder<>(this); + result.stateBasedImmutableWhenCommitting = toImmutableWhenCommitting; + return result; } - public void setStateBasedImmutableWhenCommitting(boolean toImmutableWhenCommitting) { - this.stateBasedImmutableWhenCommitting = toImmutableWhenCommitting; + public VersionedMapStoreBuilder setStateBasedNodeSharingStrategy(StateStorageStrategy strategy) { + var result = new VersionedMapStoreBuilder<>(this); + result.stateBasedNodeSharingStrategy = strategy; + return result; } - public void setStateBasedNodeSharingStrategy(StateStorageStrategy strategy) { - this.stateBasedNodeSharingStrategy = strategy; + public VersionedMapStoreBuilder setDeltaStorageStrategy(DeltaStorageStrategy deltaStorageStrategy) { + var result = new VersionedMapStoreBuilder<>(this); + result.deltaStorageStrategy = deltaStorageStrategy; + return result; } public VersionedMapStore buildOne() { + if(!defaultSet) { + throw new IllegalStateException("Default value is missing!"); + } return switch (strategy) { case DELTA -> new VersionedMapStoreDeltaImpl<>( this.deltaStorageStrategy == DeltaStorageStrategy.SET, - this.defaultValue.orElseThrow()); + this.defaultValue); case STATE -> new VersionedMapStoreImpl<>( - this.hashProvider.orElseThrow(), - this.defaultValue.orElseThrow(), + this.hashProvider, + this.defaultValue, new VersionedMapStoreConfiguration( this.stateBasedImmutableWhenCommitting, this.stateBasedNodeSharingStrategy != StateStorageStrategy.NO_NODE_CACHE, @@ -60,12 +94,15 @@ public class VersionedMapStoreBuilder { } public List> buildGroup(int amount) { + if(!defaultSet) { + throw new IllegalStateException("Default value is missing!"); + } if (this.strategy == StoreStrategy.STATE && this.stateBasedNodeSharingStrategy == StateStorageStrategy.SHARED_NODE_CACHE_IN_GROUP) { return VersionedMapStoreImpl.createSharedVersionedMapStores( amount, - this.hashProvider.orElseThrow(), - this.defaultValue.orElseThrow(), + this.hashProvider, + this.defaultValue, new VersionedMapStoreConfiguration( this.stateBasedImmutableWhenCommitting, true, @@ -78,4 +115,16 @@ public class VersionedMapStoreBuilder { return result; } } + + @Override + public String toString() { + return "VersionedMapStoreBuilder{" + + "defaultValue=" + defaultValue + + ", strategy=" + strategy + + ", stateBasedImmutableWhenCommitting=" + stateBasedImmutableWhenCommitting + + ", stateBasedNodeSharingStrategy=" + stateBasedNodeSharingStrategy + + ", hashProvider=" + hashProvider + + ", deltaStorageStrategy=" + deltaStorageStrategy + + '}'; + } } -- cgit v1.2.3-54-g00ecf From 4f447bb7efd453eb6aa17fb29b8a0d7d65c03fcd Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Mon, 13 Feb 2023 01:29:28 +0100 Subject: Multiple small updates and fixes to support all upcoming tests. - AnyVersionedMap.checkIntegrity added to the superclass - Default value of the map is gettable. - Errors fixed: - Delta store failed to update reference to the previous transaction in some cases. Fixed in VersionedMapStoreDeltaImpl.java import java.util.HashMap; - Null values caused issues in UncommittedDeltaMapStore.java as putIfAbsent does not work with null. - Small fixes in DeltaDiffCursor.java and IteratorAsCursor.java --- .../tools/refinery/store/map/AnyVersionedMap.java | 5 ++ .../tools/refinery/store/map/VersionedMap.java | 2 + .../store/map/VersionedMapStoreDeltaImpl.java | 8 +- .../store/map/internal/DeltaDiffCursor.java | 2 + .../store/map/internal/IteratorAsCursor.java | 3 +- .../map/internal/UncommittedDeltaMapStore.java | 11 +-- .../store/map/internal/UncommittedDeltaStore.java | 14 ++++ .../store/map/internal/VersionedMapDeltaImpl.java | 85 ++++++++++++++++++---- .../store/map/internal/VersionedMapImpl.java | 2 + 9 files changed, 107 insertions(+), 25 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/AnyVersionedMap.java b/subprojects/store/src/main/java/tools/refinery/store/map/AnyVersionedMap.java index f82a8bb1..ead79878 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/AnyVersionedMap.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/AnyVersionedMap.java @@ -37,4 +37,9 @@ public sealed interface AnyVersionedMap extends Versioned permits VersionedMap { @SuppressWarnings("squid:S1133") @Deprecated(since = "0.0.0") boolean equals(Object obj); + + /** + * Checks the integrity of the map, and throws an exception if an inconsistency is detected. + */ + void checkIntegrity(); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java index 31985e94..08ce1dbd 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java @@ -1,6 +1,8 @@ package tools.refinery.store.map; public non-sealed interface VersionedMap extends AnyVersionedMap { + V getDefaultValue(); + V get(K key); Cursor getAll(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java index 98dec2bb..e556a8bb 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java @@ -49,21 +49,22 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore return states.get(state); } - public void getPath(long to, List[]> forwardTransactions) { + public MapTransaction getPath(long to, List[]> forwardTransactions) { MapTransaction toTransaction = getState(to); while (toTransaction != null) { forwardTransactions.add(toTransaction.deltas()); toTransaction = toTransaction.parent(); } + return toTransaction; } - public void getPath(long from, long to, + public MapTransaction getPath(long from, long to, List[]> backwardTransactions, List[]> forwardTransactions) { MapTransaction fromTransaction = getState(from); MapTransaction toTransaction = getState(to); while (fromTransaction != toTransaction) { - if (fromTransaction == null || fromTransaction.version() < toTransaction.version()) { + if (fromTransaction == null || (toTransaction != null && fromTransaction.version() < toTransaction.version())) { forwardTransactions.add(toTransaction.deltas()); toTransaction = toTransaction.parent(); } else { @@ -71,6 +72,7 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore fromTransaction = fromTransaction.parent(); } } + return toTransaction; } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java index 75180bf9..49ea1f67 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java @@ -62,6 +62,7 @@ public class DeltaDiffCursor implements DiffCursor { return this.direction && listIndex == -1; } + @Override public boolean move() { if (isTerminated()) { @@ -74,6 +75,7 @@ public class DeltaDiffCursor implements DiffCursor { } else { if (listIndex-1 >= 0) { listIndex--; + arrayIndex = 0; return true; } else { listIndex = -1; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java index 4a8e9709..c1a0aec4 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java @@ -18,7 +18,6 @@ public class IteratorAsCursor implements Cursor { public IteratorAsCursor(VersionedMap source, Map current) { this.iterator = current.entrySet().iterator(); this.source = source; - move(); } @Override @@ -38,7 +37,7 @@ public class IteratorAsCursor implements Cursor { @Override public boolean move() { - terminated = iterator.hasNext(); + terminated = !iterator.hasNext(); if (terminated) { this.key = null; this.value = null; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java index 73df5080..31423b1c 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java @@ -1,7 +1,6 @@ package tools.refinery.store.map.internal; -import java.util.HashMap; -import java.util.Map; +import java.util.*; import java.util.Map.Entry; import tools.refinery.store.map.VersionedMap; @@ -16,7 +15,9 @@ public class UncommittedDeltaMapStore implements UncommittedDeltaStore implements UncommittedDeltaStore[] deltas = new MapDelta[uncommittedOldValues.size()]; + MapDelta[] deltas = new MapDelta[uncommittedOldValues.size()]; int i = 0; for (Entry entry : uncommittedOldValues.entrySet()) { final K key = entry.getKey(); final V oldValue = entry.getValue(); final V newValue = source.get(key); - deltas[i] = new MapDelta<>(key, oldValue, newValue); + deltas[i++] = new MapDelta<>(key, oldValue, newValue); } return deltas; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java index 37e5817c..7b017c8e 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java @@ -7,4 +7,18 @@ public interface UncommittedDeltaStore { MapDelta[] extractAndDeleteDeltas(); + default void checkIntegrity() { + MapDelta[] extractedDeltas = extractDeltas(); + if(extractedDeltas != null) { + for(var uncommittedOldValue : extractedDeltas) { + if(uncommittedOldValue == null) { + throw new IllegalArgumentException("Null entry in deltas!"); + } + if(uncommittedOldValue.getKey() == null) { + throw new IllegalStateException("Null key in deltas!"); + } + } + } + } + } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java index 6f2996e1..d09e54ba 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java @@ -19,13 +19,18 @@ public class VersionedMapDeltaImpl implements VersionedMap { this.defaultValue = defaultValue; current = new HashMap<>(); - if(summarizeChanges) { + if (summarizeChanges) { this.uncommittedStore = new UncommittedDeltaMapStore<>(this); } else { this.uncommittedStore = new UncommittedDeltaArrayStore<>(); } } + @Override + public V getDefaultValue() { + return defaultValue; + } + @Override public long commit() { MapDelta[] deltas = uncommittedStore.extractAndDeleteDeltas(); @@ -43,16 +48,18 @@ public class VersionedMapDeltaImpl implements VersionedMap { } // 2. get common ancestor + final MapTransaction parent; List[]> forward = new ArrayList<>(); if (this.previous == null) { - this.store.getPath(state, forward); + parent = this.store.getPath(state, forward); this.forward(forward); } else { List[]> backward = new ArrayList<>(); - this.store.getPath(this.previous.version(), state, backward, forward); + parent = this.store.getPath(this.previous.version(), state, backward, forward); this.backward(backward); this.forward(forward); } + this.previous = parent; } protected void forward(List[]> changes) { @@ -70,14 +77,28 @@ public class VersionedMapDeltaImpl implements VersionedMap { protected void forward(MapDelta[] changes) { for (int i = 0; i < changes.length; i++) { final MapDelta change = changes[i]; - current.put(change.getKey(), change.getNewValue()); + K key = change.getKey(); + V newValue = change.getNewValue(); + + if(newValue == defaultValue) { + current.remove(key); + } else { + current.put(key,newValue); + } } } protected void backward(MapDelta[] changes) { for (int i = changes.length - 1; i >= 0; i--) { final MapDelta change = changes[i]; - current.put(change.getKey(), change.getOldValue()); + K key = change.getKey(); + V oldValue = change.oldValue(); + + if(oldValue == defaultValue) { + current.remove(key); + } else { + current.put(key,oldValue); + } } } @@ -93,26 +114,46 @@ public class VersionedMapDeltaImpl implements VersionedMap { @Override public V put(K key, V value) { - if (value == defaultValue) { - V res = current.remove(key); + final V oldValue; + if (Objects.equals(value, defaultValue)) { + final V res = current.remove(key); if (res == null) { - // no changes - return defaultValue; + // no changes: default > default + oldValue = defaultValue; } else { - uncommittedStore.processChange(key, res, value); - return res; + oldValue = res; } } else { - V oldValue = current.put(key, value); + final var mapValue = current.put(key, value); + if (mapValue == null) { + oldValue = defaultValue; + } else { + oldValue = mapValue; + } + } + if(!Objects.equals(oldValue,value)) { uncommittedStore.processChange(key, oldValue, value); - return oldValue; } + return oldValue; } @Override public void putAll(Cursor cursor) { - throw new UnsupportedOperationException(); - + if (cursor.getDependingMaps().contains(this)) { + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + while (cursor.move()) { + keys.add(cursor.getKey()); + values.add(cursor.getValue()); + } + for (int i = 0; i < keys.size(); i++) { + this.put(keys.get(i), values.get(i)); + } + } else { + while (cursor.move()) { + this.put(cursor.getKey(), cursor.getValue()); + } + } } @Override @@ -156,4 +197,18 @@ public class VersionedMapDeltaImpl implements VersionedMap { throw new UnsupportedOperationException("Comparing different map implementations is ineffective."); } } + + @Override + public void checkIntegrity() { + this.uncommittedStore.checkIntegrity(); + + for (var entry : this.current.entrySet()) { + var value = entry.getValue(); + if (value == this.defaultValue) { + throw new IllegalStateException("Default value stored in map!"); + } else if (value == null) { + throw new IllegalStateException("null value stored in map!"); + } + } + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java index 674ffc47..fb359431 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java @@ -43,6 +43,7 @@ public class VersionedMapImpl implements VersionedMap { this.root = data; } + @Override public V getDefaultValue() { return defaultValue; } @@ -141,6 +142,7 @@ public class VersionedMapImpl implements VersionedMap { } } + @Override public void checkIntegrity() { if (this.root != null) { this.root.checkIntegrity(hashProvider, defaultValue, 0); -- cgit v1.2.3-54-g00ecf From 71809f2867baad1acb373cee0fa1f86394c14370 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Mon, 13 Feb 2023 22:23:05 +0100 Subject: Potential synchronization issue fixed in VersionedMapStoreDeltaImpl.getStates --- .../main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java index e556a8bb..f41a2d75 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java @@ -78,7 +78,7 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore @Override public synchronized Set getStates() { - return states.keySet(); + return new HashSet<>(states.keySet()); } @Override -- cgit v1.2.3-54-g00ecf From b5026e94f87bb619ea66d84e7799e089cfb8fa07 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 14 Feb 2023 00:32:26 +0100 Subject: Nasty error fixed in VersionedMapStoreDeltaImpl.java when setting the new parent state --- .../tools/refinery/store/map/VersionedMapStoreDeltaImpl.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java index f41a2d75..31cdbf95 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java @@ -50,19 +50,22 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore } public MapTransaction getPath(long to, List[]> forwardTransactions) { - MapTransaction toTransaction = getState(to); + final MapTransaction target = getState(to); + MapTransaction toTransaction = target; while (toTransaction != null) { forwardTransactions.add(toTransaction.deltas()); toTransaction = toTransaction.parent(); } - return toTransaction; + return target; } public MapTransaction getPath(long from, long to, List[]> backwardTransactions, List[]> forwardTransactions) { MapTransaction fromTransaction = getState(from); - MapTransaction toTransaction = getState(to); + final MapTransaction target = getState(to); + MapTransaction toTransaction = target; + while (fromTransaction != toTransaction) { if (fromTransaction == null || (toTransaction != null && fromTransaction.version() < toTransaction.version())) { forwardTransactions.add(toTransaction.deltas()); @@ -72,7 +75,7 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore fromTransaction = fromTransaction.parent(); } } - return toTransaction; + return target; } -- cgit v1.2.3-54-g00ecf From 6598f296d79382a7eaf6ec42819d0123b0acc0d1 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 14 Feb 2023 00:43:00 +0100 Subject: Test environment cannot rely upon the order of elements in a map since VersionedMapDelta appeared. --- .../store/map/tests/utils/MapTestEnvironment.java | 38 +++++++++------------- 1 file changed, 15 insertions(+), 23 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java index 30f38201..69ae811e 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java @@ -55,23 +55,9 @@ public class MapTestEnvironment { public static void compareTwoMaps(String title, VersionedMap map1, VersionedMap map2, List errors) { + map1.checkIntegrity(); + map2.checkIntegrity(); assertEqualsList(map1.getSize(), map2.getSize(), title + ": Sizes not equal", errors); - - Cursor cursor1 = map1.getAll(); - Cursor cursor2 = map2.getAll(); - while (!cursor1.isTerminated()) { - if (cursor2.isTerminated()) { - fail("cursor 2 terminated before cursor1"); - } - assertEqualsList(cursor1.getKey(), cursor2.getKey(), title + ": Keys not equal", errors); - assertEqualsList(cursor2.getValue(), cursor2.getValue(), title + ": Values not equal", errors); - cursor1.move(); - cursor2.move(); - } - if (!cursor2.isTerminated()) { - fail("cursor 1 terminated before cursor 2"); - } - for (var mode : ContentHashCode.values()) { assertEqualsList(map1.contentHashCode(mode), map2.contentHashCode(mode), title + ": " + mode + " hashCode check", errors); @@ -107,29 +93,35 @@ public class MapTestEnvironment { } } - public VersionedMapImpl sut; - Map oracle = new HashMap(); + final private VersionedMap sut; + final private V defaultValue; + Map oracle = new HashMap<>(); - public MapTestEnvironment(VersionedMapImpl sut) { + public MapTestEnvironment(VersionedMap sut) { this.sut = sut; + this.defaultValue = sut.getDefaultValue(); } public void put(K key, V value) { V oldSutValue = sut.put(key, value); V oldOracleValue; - if (value != sut.getDefaultValue()) { + if (value != defaultValue) { oldOracleValue = oracle.put(key, value); } else { oldOracleValue = oracle.remove(key); } - if (oldSutValue == sut.getDefaultValue() && oldOracleValue != null) { + if (oldSutValue == defaultValue && oldOracleValue != null) { fail("After put, SUT old nodeId was default, but oracle old value was " + oldOracleValue); } - if (oldSutValue != sut.getDefaultValue()) { + if (oldSutValue != defaultValue) { assertEquals(oldOracleValue, oldSutValue); } } + public long commit(){ + return sut.commit(); + } + public void checkEquivalence(String title) { // 0. Checking integrity try { @@ -176,7 +168,7 @@ public class MapTestEnvironment { long sutSize = sut.getSize(); if (oracleSize != sutSize || oracleSize != elementsInSutEntrySet) { printComparison(); - fail(title + ": Non-equivalent size() result: SUT.getSize()=" + sutSize + ", SUT.entryset.size=" + fail(title + ": Non-equivalent size() result: SUT.getSize()=" + sutSize + ", SUT.entrySet.size=" + elementsInSutEntrySet + ", Oracle=" + oracleSize + "!"); } } -- cgit v1.2.3-54-g00ecf From 189d1181052fc014830ac53d0090d7f3cfbc36aa Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 14 Feb 2023 00:46:26 +0100 Subject: Fuzz test environment is parametrized by VersionedMapStoreBuilder configurations --- .../store/map/tests/fuzz/CommitFuzzTest.java | 34 +++++++-------- .../map/tests/fuzz/ContentEqualsFuzzTest.java | 37 ++++++++-------- .../store/map/tests/fuzz/DiffCursorFuzzTest.java | 50 ++++++++++------------ .../store/map/tests/fuzz/MultiThreadFuzzTest.java | 42 +++++++++--------- .../map/tests/fuzz/MultiThreadTestRunnable.java | 22 +++++++--- .../store/map/tests/fuzz/MutableFuzzTest.java | 35 +++++++-------- .../fuzz/MutableImmutableCompareFuzzTest.java | 2 +- .../store/map/tests/fuzz/RestoreFuzzTest.java | 32 +++++++------- .../store/map/tests/fuzz/SharedStoreFuzzTest.java | 2 +- .../map/tests/fuzz/utils/FuzzTestCollections.java | 40 +++++++++++++++-- 10 files changed, 161 insertions(+), 135 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java index b61152f5..14a9e2e0 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java @@ -11,10 +11,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import tools.refinery.store.map.ContinousHashProvider; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreImpl; -import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.VersionedMapStoreBuilder; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -23,13 +21,11 @@ import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; class CommitFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, - boolean nullDefault, int commitFrequency, - boolean evilHash) { + boolean nullDefault, int commitFrequency, VersionedMapStoreBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); - VersionedMapImpl sut = (VersionedMapImpl) store.createMap(); + VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); + var sut = store.createMap(); MapTestEnvironment e = new MapTestEnvironment<>(sut); Random r = new Random(seed); @@ -52,37 +48,37 @@ class CommitFuzzTest { } MapTestEnvironment.printStatus(scenario, index, steps, null); if (index % commitFrequency == 0) { - e.sut.commit(); + e.commit(); } } } - @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5} " + - "seed={6} evil-hash={7}") + public static final String title = "Commit {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5} " + + "seed={6} config={7}"; + + @ParameterizedTest(name = title) @MethodSource @Timeout(value = 10) @Tag("fuzz") void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, - boolean evilHash) { + int seed, VersionedMapStoreBuilder builder) { runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - nullDefault, commitFrequency, evilHash); + nullDefault, commitFrequency, builder); } static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, - commitFrequencyOptions, randomSeedOptions, evilHashOptions); + commitFrequencyOptions, randomSeedOptions, storeConfigs); } - @ParameterizedTest(name = "Commit {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5} " + - "seed={6} evil-hash={7}") + @ParameterizedTest(name = title) @MethodSource @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, boolean evilHash) { + int seed, VersionedMapStoreBuilder builder) { runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - nullDefault, commitFrequency, evilHash); + nullDefault, commitFrequency, builder); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java index c17b0a95..b462ed40 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java @@ -6,7 +6,6 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import tools.refinery.store.map.*; -import tools.refinery.store.map.internal.VersionedMapImpl; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -22,20 +21,19 @@ import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; class ContentEqualsFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, - boolean nullDefault, int commitFrequency, - boolean evilHash) { + boolean nullDefault, int commitFrequency, VersionedMapStoreBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); + Random r = new Random(seed); - iterativeRandomPutsAndCommitsThenCompare(scenario, chp, steps, maxKey, values, r, commitFrequency); + iterativeRandomPutsAndCommitsThenCompare(scenario, builder, steps, maxKey, values, r, commitFrequency); } - private void iterativeRandomPutsAndCommitsThenCompare(String scenario, ContinousHashProvider chp, + private void iterativeRandomPutsAndCommitsThenCompare(String scenario, VersionedMapStoreBuilder builder, int steps, int maxKey, String[] values, Random r, int commitFrequency) { - VersionedMapStore store1 = new VersionedMapStoreImpl<>(chp, values[0]); + VersionedMapStore store1 = builder.setDefaultValue(values[0]).buildOne(); VersionedMap sut1 = store1.createMap(); // Fill one map @@ -65,7 +63,7 @@ class ContentEqualsFuzzTest { // Randomize the order of the content Collections.shuffle(content, r); - VersionedMapStore store2 = new VersionedMapStoreImpl<>(chp, values[0]); + VersionedMapStore store2 = builder.setDefaultValue(values[0]).buildOne(); VersionedMap sut2 = store2.createMap(); int index2 = 1; for (SimpleEntry entry : content) { @@ -75,38 +73,39 @@ class ContentEqualsFuzzTest { } // Check the integrity of the maps - ((VersionedMapImpl) sut1).checkIntegrity(); - ((VersionedMapImpl) sut2).checkIntegrity(); + sut1.checkIntegrity(); + sut2.checkIntegrity(); // Compare the two maps MapTestEnvironment.compareTwoMaps(scenario, sut1, sut2); } - @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit frequency={5}" + - "seed={6} evil-hash={7}") + public static final String title = "Compare {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit frequency={5}" + + "seed={6} config={7}"; + + @ParameterizedTest(name = title) @MethodSource @Timeout(value = 10) @Tag("fuzz") void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, boolean evilHash) { + int seed, VersionedMapStoreBuilder builder) { runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - nullDefault, commitFrequency, evilHash); + nullDefault, commitFrequency, builder); } static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, - commitFrequencyOptions, randomSeedOptions, evilHashOptions); + commitFrequencyOptions, randomSeedOptions, storeConfigs); } - @ParameterizedTest(name = "Compare {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit frequency={5}" + - "seed={6} evil-hash={7}") + @ParameterizedTest(name = title) @MethodSource @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, int commitFrequency, - int seed, boolean evilHash) { + int seed, VersionedMapStoreBuilder builder) { runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - defaultNull, commitFrequency, evilHash); + defaultNull, commitFrequency, builder); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java index 90aa8e01..bf409a74 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java @@ -1,33 +1,26 @@ package tools.refinery.store.map.tests.fuzz; -import static org.junit.jupiter.api.Assertions.fail; -import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; - -import java.util.Random; -import java.util.stream.Stream; - import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; - -import tools.refinery.store.map.ContinousHashProvider; -import tools.refinery.store.map.DiffCursor; -import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreImpl; -import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.*; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; +import java.util.Random; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.fail; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; + class DiffCursorFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, - boolean nullDefault, int commitFrequency, - boolean evilHash) { + boolean nullDefault, int commitFrequency, VersionedMapStoreBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); + VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); iterativeRandomPutsAndCommitsThenDiffCursor(scenario, store, steps, maxKey, values, seed, commitFrequency); } @@ -35,7 +28,7 @@ class DiffCursorFuzzTest { int steps, int maxKey, String[] values, int seed, int commitFrequency) { // 1. build a map with versions Random r = new Random(seed); - VersionedMapImpl versioned = (VersionedMapImpl) store.createMap(); + VersionedMap versioned = store.createMap(); int largestCommit = -1; for (int i = 0; i < steps; i++) { @@ -56,7 +49,7 @@ class DiffCursorFuzzTest { System.out.println(scenario + ":" + index + "/" + steps + " building finished"); } // 2. create a non-versioned map, - VersionedMapImpl moving = (VersionedMapImpl) store.createMap(); + VersionedMap moving = store.createMap(); Random r2 = new Random(seed + 1); final int diffTravelFrequency = commitFrequency * 2; @@ -88,31 +81,32 @@ class DiffCursorFuzzTest { } - @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + - "commit frequency={5} seed={6} evil-hash={7}") + public static final String title = "DiffCursor {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + + "commit frequency={5} seed={6} config={7}"; + + @ParameterizedTest(name = title) @MethodSource @Timeout(value = 10) @Tag("fuzz") void parametrizedFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, boolean evilHash) { + int seed, VersionedMapStoreBuilder builder) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, - noKeys, noValues, nullDefault, commitFrequency, evilHash); + noKeys, noValues, nullDefault, commitFrequency, builder); } static Stream parametrizedFuzz() { - return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, - commitFrequencyOptions, randomSeedOptions, evilHashOptions); + return FuzzTestUtils.permutationWithSize(new Object[]{100}, keyCounts, valueCounts, nullDefaultOptions, + commitFrequencyOptions, randomSeedOptions, storeConfigs); } - @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + - "commit frequency={5} seed={6} evil-hash={7}") + @ParameterizedTest(name = title) @MethodSource @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, boolean evilHash) { + int seed, VersionedMapStoreBuilder builder) { runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - nullDefault, commitFrequency, evilHash); + nullDefault, commitFrequency, builder); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java index 9325a938..ec2224b4 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java @@ -15,27 +15,24 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import tools.refinery.store.map.ContinousHashProvider; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreImpl; +import tools.refinery.store.map.VersionedMapStoreBuilder; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; class MultiThreadFuzzTest { - public static final int noThreads = 32; + public static final int noThreads = 10; - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, - boolean nullDefault, int commitFrequency, - boolean evilHash) { + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, VersionedMapStoreBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); + VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); // initialize runnables MultiThreadTestRunnable[] runnables = new MultiThreadTestRunnable[noThreads]; for (int i = 0; i < noThreads; i++) { - runnables[i] = new MultiThreadTestRunnable(scenario + "-T" + (i + 1), store, steps, maxKey, values, seed, commitFrequency); + runnables[i] = new MultiThreadTestRunnable(scenario + "-T" + (i + 1), store, steps, maxKey, values, seed + , commitFrequency); } // initialize threads @@ -46,7 +43,8 @@ class MultiThreadFuzzTest { // start threads; for (int i = 0; i < noThreads; i++) { - threads[i].start(); + runnables[i].run(); + //threads[i].start(); } // wait all the threads; @@ -67,32 +65,32 @@ class MultiThreadFuzzTest { assertEquals(Collections.EMPTY_LIST, errors); } - @ParameterizedTest(name = "MultiThread {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit " + - "frequency={5} seed={6} evil-hash={7}") + static final String title = "MultiThread {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit " + + "frequency={5} seed={6} config={7}"; + + @ParameterizedTest(name = title) @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, int commitFrequency, - int seed, boolean evilHash) { + void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, + int commitFrequency, int seed, VersionedMapStoreBuilder builder) { runFuzzTest("MultiThreadS" + steps + "K" + noKeys + "V" + noValues + defaultNull + "CF" + commitFrequency + - "s" + seed, seed, steps, noKeys, noValues, defaultNull, commitFrequency, evilHash); + "s" + seed, seed, steps, noKeys, noValues, defaultNull, commitFrequency, builder); } static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, - new Object[]{10, 100}, randomSeedOptions, - evilHashOptions); + new Object[]{10, 100}, randomSeedOptions, storeConfigs); } - @ParameterizedTest(name = "MultiThread {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit " + - "frequency={5} seed={6} evil-hash={7}") + @ParameterizedTest(name = title) @MethodSource @Tag("fuzz") @Tag("slow") - void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, - boolean evilHash) { + void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, + int commitFrequency, int seed, VersionedMapStoreBuilder builder) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - nullDefault, commitFrequency, evilHash); + nullDefault, commitFrequency, builder); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java index 4415e4e5..f449ca97 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java @@ -8,8 +8,8 @@ import java.util.List; import java.util.Map; import java.util.Random; +import tools.refinery.store.map.VersionedMap; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.internal.VersionedMapImpl; import tools.refinery.store.map.tests.utils.MapTestEnvironment; public class MultiThreadTestRunnable implements Runnable { @@ -45,9 +45,17 @@ public class MultiThreadTestRunnable implements Runnable { @Override public void run() { + try{ + task(); + } catch(Exception e) { + e.printStackTrace(); + } + } + + private void task() { // 1. build a map with versions Random r = new Random(seed); - VersionedMapImpl versioned = (VersionedMapImpl) store.createMap(); + VersionedMap versioned = store.createMap(); Map index2Version = new HashMap<>(); for (int i = 0; i < steps; i++) { @@ -67,7 +75,7 @@ public class MultiThreadTestRunnable implements Runnable { MapTestEnvironment.printStatus(scenario, index, steps, "building"); } // 2. create a non-versioned - VersionedMapImpl reference = (VersionedMapImpl) store.createMap(); + VersionedMap reference = store.createMap(); r = new Random(seed); Random r2 = new Random(seed + 1); @@ -84,13 +92,17 @@ public class MultiThreadTestRunnable implements Runnable { // go back to an existing state and compare to the reference if (index % (commitFrequency) == 0) { versioned.restore(index2Version.get(i)); - MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference, versioned, errors); + MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference, versioned, null); // go back to a random state (probably created by another thread) List states = new ArrayList<>(store.getStates()); + states.sort(Long::compare); Collections.shuffle(states, r2); for (Long state : states.subList(0, Math.min(states.size(), 100))) { - versioned.restore(state); + long x = state; + versioned.restore(x); + var clean = store.createMap(x); + MapTestEnvironment.compareTwoMaps(scenario + ":" + index, clean, versioned, null); } versioned.restore(index2Version.get(i)); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java index ab37ef83..bdf72ce4 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java @@ -12,21 +12,17 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import tools.refinery.store.map.ContinousHashProvider; -import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreImpl; -import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.*; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; class MutableFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, - boolean nullDefault, boolean evilHash) { + boolean nullDefault, VersionedMapStoreBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); - VersionedMapImpl sut = (VersionedMapImpl) store.createMap(); + VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); + VersionedMap sut = store.createMap(); MapTestEnvironment e = new MapTestEnvironment<>(sut); Random r = new Random(seed); @@ -52,33 +48,34 @@ class MutableFuzzTest { } } - @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} seed={5} " + - "evil-hash={6}") + final String title = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} seed={5} " + + "config={6}"; + + @ParameterizedTest(name = title) @MethodSource @Timeout(value = 10) @Tag("fuzz") void parametrizedFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, int seed, - boolean evilHash) { + VersionedMapStoreBuilder builder) { runFuzzTest( - "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed + "H" + (evilHash ? "Evil" : "Normal"), - seed, steps, noKeys, noValues, defaultNull, evilHash); + "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, + seed, steps, noKeys, noValues, defaultNull, builder); } static Stream parametrizedFuzz() { return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, - randomSeedOptions, evilHashOptions); + randomSeedOptions, storeConfigs); } - @ParameterizedTest(name = "Mutable {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} seed={5} " + - "evil-hash={6}") + @ParameterizedTest(name = title) @MethodSource @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int seed, - boolean evilHash) { + VersionedMapStoreBuilder builder) { runFuzzTest( - "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed + "H" + (evilHash ? "Evil" : "Normal"), - seed, steps, noKeys, noValues, nullDefault, evilHash); + "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, + seed, steps, noKeys, noValues, nullDefault, builder); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java index b58b06f9..cee15fe1 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java @@ -71,7 +71,7 @@ class MutableImmutableCompareFuzzTest { static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, - commitFrequencyOptions, randomSeedOptions, evilHashOptions); + commitFrequencyOptions, randomSeedOptions, new Object[]{false, true}); } @ParameterizedTest(name = "Mutable-Immutable Compare {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java index 77b26c41..568aaac9 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java @@ -14,9 +14,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import tools.refinery.store.map.ContinousHashProvider; -import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreImpl; +import tools.refinery.store.map.*; import tools.refinery.store.map.internal.VersionedMapImpl; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -24,11 +22,10 @@ import tools.refinery.store.map.tests.utils.MapTestEnvironment; class RestoreFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, - boolean evilHash) { + VersionedMapStoreBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); + VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); iterativeRandomPutsAndCommitsThenRestore(scenario, store, steps, maxKey, values, seed, commitFrequency); } @@ -37,7 +34,7 @@ class RestoreFuzzTest { int steps, int maxKey, String[] values, int seed, int commitFrequency) { // 1. build a map with versions Random r = new Random(seed); - VersionedMapImpl versioned = (VersionedMapImpl) store.createMap(); + VersionedMap versioned = store.createMap(); Map index2Version = new HashMap<>(); for (int i = 0; i < steps; i++) { @@ -57,7 +54,7 @@ class RestoreFuzzTest { MapTestEnvironment.printStatus(scenario, index, steps, "building"); } // 2. create a non-versioned and - VersionedMapImpl reference = (VersionedMapImpl) store.createMap(); + VersionedMap reference = store.createMap(); r = new Random(seed); for (int i = 0; i < steps; i++) { @@ -79,31 +76,32 @@ class RestoreFuzzTest { } - @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5}" + - " seed={6} evil-hash={7}") + public static final String title = "Commit {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5} " + + "seed={6} config={7}"; + + @ParameterizedTest(name = title) @MethodSource @Timeout(value = 10) @Tag("smoke") void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, boolean evilHash) { + int seed, VersionedMapStoreBuilder builder) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - nullDefault, commitFrequency, evilHash); + nullDefault, commitFrequency, builder); } static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, - commitFrequencyOptions, randomSeedOptions, evilHashOptions); + commitFrequencyOptions, randomSeedOptions, storeConfigs); } - @ParameterizedTest(name = "Restore {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit frequency={5}" + - " seed={6} evil-hash={7}") + @ParameterizedTest(name = title) @MethodSource @Tag("smoke") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, boolean evilHash) { + int seed, VersionedMapStoreBuilder builder) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - nullDefault, commitFrequency, evilHash); + nullDefault, commitFrequency, builder); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java index 4462c55b..0544687a 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java @@ -96,7 +96,7 @@ class SharedStoreFuzzTest { static Stream parametrizedFastFuzz() { return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, - commitFrequencyOptions, randomSeedOptions, evilHashOptions); + commitFrequencyOptions, randomSeedOptions, new Object[]{false, true}); } @ParameterizedTest(name = "Shared Store {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} commit " + diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java index add4ca5d..fb6b28d8 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java @@ -1,11 +1,43 @@ package tools.refinery.store.map.tests.fuzz.utils; +import tools.refinery.store.map.VersionedMapStoreBuilder; +import tools.refinery.store.map.tests.utils.MapTestEnvironment; + public final class FuzzTestCollections { public static final Object[] stepCounts = {FuzzTestUtils.FAST_STEP_COUNT}; - public static final Object[] keyCounts = {3, 32, 32 * 32}; + public static final Object[] keyCounts = {1, 32, 32 * 32}; public static final Object[] valueCounts = {2, 3}; public static final Object[] nullDefaultOptions = {false, true}; - public static final Object[] commitFrequencyOptions = {1, 10, 100}; - public static final Object[] randomSeedOptions = {1, 2, 3}; - public static final Object[] evilHashOptions = {false, true}; + public static final Object[] commitFrequencyOptions = {10, 10, 100}; + public static final Object[] randomSeedOptions = {1/*, 2, 3*/}; + public static final Object[] storeConfigs = { + // State based + VersionedMapStoreBuilder.builder() + .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) + .setStateBasedImmutableWhenCommitting(true) + .setHashProvider(MapTestEnvironment.prepareHashProvider(false)) + .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy.SHARED_NODE_CACHE), + VersionedMapStoreBuilder.builder() + .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) + .setStateBasedImmutableWhenCommitting(true) + .setHashProvider(MapTestEnvironment.prepareHashProvider(true)) + .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy.SHARED_NODE_CACHE), + VersionedMapStoreBuilder.builder() + .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) + .setStateBasedImmutableWhenCommitting(false) + .setHashProvider(MapTestEnvironment.prepareHashProvider(false)) + .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy.SHARED_NODE_CACHE), + VersionedMapStoreBuilder.builder() + .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) + .setStateBasedImmutableWhenCommitting(false) + .setHashProvider(MapTestEnvironment.prepareHashProvider(false)) + .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy.NO_NODE_CACHE), + // Delta based + VersionedMapStoreBuilder.builder() + .setStrategy(VersionedMapStoreBuilder.StoreStrategy.DELTA) + .setDeltaStorageStrategy(VersionedMapStoreBuilder.DeltaStorageStrategy.SET), + VersionedMapStoreBuilder.builder() + .setStrategy(VersionedMapStoreBuilder.StoreStrategy.DELTA) + .setDeltaStorageStrategy(VersionedMapStoreBuilder.DeltaStorageStrategy.LIST) + }; } -- cgit v1.2.3-54-g00ecf From f0f04091e31f80f253f9129ca3a7191d0af6af1e Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Thu, 16 Feb 2023 00:41:09 +0100 Subject: Additional tests for delta restoration --- .../refinery/store/map/tests/MapUnitTests.java | 72 ++++++++++++++++++++++ .../store/map/tests/fuzz/SingleThreadFuzzTest.java | 61 ++++++++++++++++++ 2 files changed, 133 insertions(+) create mode 100644 subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java (limited to 'subprojects') diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java index 77c62305..2216db76 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java @@ -2,6 +2,7 @@ package tools.refinery.store.map.tests; import org.junit.jupiter.api.Test; import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.map.VersionedMapStoreBuilder; import tools.refinery.store.map.VersionedMapStoreImpl; import tools.refinery.store.model.TupleHashProvider; import tools.refinery.store.tuple.Tuple; @@ -18,4 +19,75 @@ class MapUnitTests { var out2 = map.put(Tuple.of(1), true); assertEquals(false, out2); } + + @Test + void deltaRestoreTest() { + VersionedMapStore store = + VersionedMapStoreBuilder.builder().setDefaultValue("x").buildOne(); + var map = store.createMap(); + map.put(1,"val"); + var version1 = map.commit(); + map.put(1,"x"); + map.restore(version1); + System.out.println(map.getSize()); + assertEquals(1,map.getSize()); + } + + @Test + void deltaRestoreTest2() { + VersionedMapStore store = + VersionedMapStoreBuilder.builder().setDefaultValue("x").buildOne(); + var map = store.createMap(); + map.put(1,"x"); + var version1 = map.commit(); + map.put(1,"1"); + map.restore(version1); + System.out.println(map.getSize()); + assertEquals(0,map.getSize()); + } + @Test + void deltaRestoreTest3() { + VersionedMapStore store = + VersionedMapStoreBuilder.builder().setDefaultValue("x").buildOne(); + var map = store.createMap(); + map.commit(); + map.put(1,"1"); + map.put(2,"x"); + assertEquals(1,map.getSize()); + var version1 = map.commit(); + map.put(1,"x"); + assertEquals(0,map.getSize()); + map.put(2,"2"); + assertEquals(1,map.getSize()); + map.put(2,"x"); + assertEquals(0,map.getSize()); + var version2 = map.commit(); + map.restore(version1); + assertEquals(1,map.getSize()); + map.restore(version2); + assertEquals(0,map.getSize()); + } + + @Test + void deltaRestoreTest4() { + VersionedMapStore store = + VersionedMapStoreBuilder.builder().setDefaultValue("x").buildOne(); + var map = store.createMap(); + map.commit(); + map.put(1,"1"); + map.put(2,"x"); + assertEquals(1,map.getSize()); + var version1 = map.commit(); + map.put(1,"x"); + assertEquals(0,map.getSize()); + map.put(2,"2"); + assertEquals(1,map.getSize()); + map.put(2,"x"); + assertEquals(0,map.getSize()); + var version2 = map.commit(); + map.restore(version1); + assertEquals(1,map.getSize()); + map.restore(version2); + assertEquals(0,map.getSize()); + } } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java new file mode 100644 index 00000000..e7d49227 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java @@ -0,0 +1,61 @@ +package tools.refinery.store.map.tests.fuzz; + +import org.junit.jupiter.api.Tag; +import org.junit.jupiter.api.Timeout; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.map.VersionedMapStoreBuilder; +import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; +import tools.refinery.store.map.tests.utils.MapTestEnvironment; + +import java.util.stream.Stream; + +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; + +class SingleThreadFuzzTest { + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, VersionedMapStoreBuilder builder) { + String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); + + VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); + + // initialize runnables + MultiThreadTestRunnable runnable = new MultiThreadTestRunnable(scenario, store, steps, maxKey, values, seed, commitFrequency); + + // start threads; + runnable.run(); + } + + static final String title = "SingleThread {index}/{0} Steps={1} Keys={2} Values={3} defaultNull={4} commit " + + "frequency={5} seed={6} config={7}"; + + @ParameterizedTest(name = title) + @MethodSource + @Timeout(value = 10) + @Tag("fuzz") + void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, + int commitFrequency, int seed, VersionedMapStoreBuilder builder) { + runFuzzTest("SingleThreadS" + steps + "K" + noKeys + "V" + noValues + defaultNull + "CF" + commitFrequency + + "s" + seed, seed, steps, noKeys, noValues, defaultNull, commitFrequency, builder); + } + + static Stream parametrizedFastFuzz() { + return FuzzTestUtils.permutationWithSize(stepCounts, keyCounts, valueCounts, nullDefaultOptions, + new Object[]{10, 100}, randomSeedOptions, storeConfigs); + } + + @ParameterizedTest(name = title) + @MethodSource + @Tag("fuzz") + @Tag("slow") + void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, + int commitFrequency, int seed, VersionedMapStoreBuilder builder) { + runFuzzTest("SingleThreadS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, + nullDefault, commitFrequency, builder); + } + + static Stream parametrizedSlowFuzz() { + return FuzzTestUtils.changeStepCount(RestoreFuzzTest.parametrizedFastFuzz(), 1); + } +} -- cgit v1.2.3-54-g00ecf From 44c4c918d7e34cada66f6cbe523a3357a83c9b77 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Mon, 20 Feb 2023 01:29:45 +0100 Subject: Initialization bugs with empty DeltaDiffCursor fixed --- .../refinery/store/map/internal/DeltaDiffCursor.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java index 49ea1f67..8ddca8ec 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java @@ -11,6 +11,7 @@ public class DeltaDiffCursor implements DiffCursor { final List[]> backwardTransactions; final List[]> forwardTransactions; + boolean started; /** * Denotes the direction of traversal. False means backwards, true means * forward. @@ -35,6 +36,7 @@ public class DeltaDiffCursor implements DiffCursor { direction = true; listIndex = -1; } + started = false; } protected MapDelta getCurrentDelta() { @@ -65,7 +67,10 @@ public class DeltaDiffCursor implements DiffCursor { @Override public boolean move() { - if (isTerminated()) { + if(!started) { + started = true; + return !isTerminated(); + } else if (isTerminated()) { return false; } else { if (this.direction) { @@ -119,11 +124,19 @@ public class DeltaDiffCursor implements DiffCursor { @Override public V getFromValue() { - return getCurrentDelta().getOldValue(); + if(this.direction) { + return getCurrentDelta().getOldValue(); + } else { + return getCurrentDelta().getNewValue(); + } } @Override public V getToValue() { - return getCurrentDelta().getNewValue(); + if(this.direction) { + return getCurrentDelta().getNewValue(); + } else { + return getCurrentDelta().getOldValue(); + } } } -- cgit v1.2.3-54-g00ecf From 89e142514ae45d793c7dbe38f728b33451261338 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 18 Jul 2023 15:38:59 +0200 Subject: Fixing long-standing bug with state based diff cursor. By implementing an InOrderMapCursor cursor, and a MapDiffCursor that synchronize two cursors. --- .../refinery/store/map/VersionedMapStoreImpl.java | 17 +- .../refinery/store/map/internal/ImmutableNode.java | 74 ++++- .../store/map/internal/InOrderMapCursor.java | 141 +++++++++ .../refinery/store/map/internal/MapCursor.java | 59 ---- .../refinery/store/map/internal/MapDiffCursor.java | 316 ++++++++++++--------- .../refinery/store/map/internal/MutableNode.java | 111 +++++--- .../tools/refinery/store/map/internal/Node.java | 2 + .../store/map/internal/VersionedMapImpl.java | 20 +- .../model/internal/VersionedInterpretation.java | 15 +- .../store/map/tests/InOrderCursorTest.java | 49 ++++ .../store/map/tests/fuzz/DiffCursorFuzzTest.java | 128 +++++---- .../map/tests/fuzz/utils/FuzzTestCollections.java | 12 +- .../store/map/tests/utils/MapTestEnvironment.java | 10 +- 13 files changed, 611 insertions(+), 343 deletions(-) create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java index 113874e7..beeed110 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java @@ -1,9 +1,6 @@ package tools.refinery.store.map; -import tools.refinery.store.map.internal.ImmutableNode; -import tools.refinery.store.map.internal.MapDiffCursor; -import tools.refinery.store.map.internal.Node; -import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.internal.*; import java.util.*; @@ -93,7 +90,7 @@ public class VersionedMapStoreImpl implements VersionedMapStore { } else { ArrayList existingKeys = new ArrayList<>(states.keySet()); Collections.sort(existingKeys); - throw new IllegalArgumentException("Store does not contain state " + state + "! Avaliable states: " + throw new IllegalArgumentException("Store does not contain state " + state + "! Available states: " + Arrays.toString(existingKeys.toArray())); } } @@ -118,10 +115,10 @@ public class VersionedMapStoreImpl implements VersionedMapStore { @Override public DiffCursor getDiffCursor(long fromState, long toState) { - VersionedMap map1 = createMap(fromState); - VersionedMap map2 = createMap(toState); - Cursor cursor1 = map1.getAll(); - Cursor cursor2 = map2.getAll(); - return new MapDiffCursor<>(this.hashProvider, this.defaultValue, cursor1, cursor2); + VersionedMapImpl map1 = (VersionedMapImpl) createMap(fromState); + VersionedMapImpl map2 = (VersionedMapImpl) createMap(toState); + InOrderMapCursor cursor1 = new InOrderMapCursor<>(map1); + InOrderMapCursor cursor2 = new InOrderMapCursor<>(map2); + return new MapDiffCursor<>(this.defaultValue, cursor1, cursor2); } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java index 914bab08..92446711 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java @@ -15,7 +15,7 @@ public class ImmutableNode extends Node { */ final int nodeMap; /** - * Stores Keys, Values, and subnodes. Structure: (K,V)*,NODE; NODES are stored + * Stores Keys, Values, and sub-nodes. Structure: (K,V)*,NODE; NODES are stored * backwards. */ final Object[] content; @@ -65,24 +65,24 @@ public class ImmutableNode extends Node { int resultDataMap = 0; int resultNodeMap = 0; final Object[] resultContent = new Object[size]; - int bitposition = 1; + int bitPosition = 1; for (int i = 0; i < FACTOR; i++) { Object key = node.content[i * 2]; if (key != null) { - resultDataMap |= bitposition; + resultDataMap |= bitPosition; resultContent[datas * 2] = key; resultContent[datas * 2 + 1] = node.content[i * 2 + 1]; datas++; } else { @SuppressWarnings("unchecked") var subnode = (Node) node.content[i * 2 + 1]; if (subnode != null) { - ImmutableNode immutableSubnode = subnode.toImmutable(cache); - resultNodeMap |= bitposition; - resultContent[size - 1 - nodes] = immutableSubnode; + ImmutableNode immutableSubNode = subnode.toImmutable(cache); + resultNodeMap |= bitPosition; + resultContent[size - 1 - nodes] = immutableSubNode; nodes++; } } - bitposition <<= 1; + bitPosition <<= 1; } final int resultHash = node.hashCode(); var newImmutable = new ImmutableNode(resultDataMap, resultNodeMap, resultContent, resultHash); @@ -130,9 +130,9 @@ public class ImmutableNode extends Node { @Override public Node putValue(K key, V value, OldValueBox oldValue, ContinousHashProvider hashProvider, V defaultValue, int hash, int depth) { int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); - int bitposition = 1 << selectedHashFragment; - if ((dataMap & bitposition) != 0) { - int keyIndex = 2 * index(dataMap, bitposition); + int bitPosition = 1 << selectedHashFragment; + if ((dataMap & bitPosition) != 0) { + int keyIndex = 2 * index(dataMap, bitPosition); @SuppressWarnings("unchecked") K keyCandidate = (K) content[keyIndex]; if (keyCandidate.equals(key)) { if (value == defaultValue) { @@ -159,8 +159,8 @@ public class ImmutableNode extends Node { return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); } } - } else if ((nodeMap & bitposition) != 0) { - int keyIndex = content.length - 1 - index(nodeMap, bitposition); + } else if ((nodeMap & bitPosition) != 0) { + int keyIndex = content.length - 1 - index(nodeMap, bitPosition); @SuppressWarnings("unchecked") var subNode = (ImmutableNode) content[keyIndex]; int newDepth = incrementDepth(depth); int newHash = newHash(hashProvider, key, hash, newDepth); @@ -253,6 +253,49 @@ public class ImmutableNode extends Node { } } + @Override + @SuppressWarnings("unchecked") + boolean moveToNextInorder(InOrderMapCursor cursor) { + if(cursor.nodeIndexStack.peek()==null) { + throw new IllegalStateException("Cursor moved to the next state when the state is empty."); + } + + int position = cursor.nodeIndexStack.peek(); + for (int index = position + 1; index < FACTOR; index++) { + final int mask = 1< subnode = (Node) this.content[this.content.length - 1 - index(nodeMap, mask)]; + cursor.nodeIndexStack.pop(); + cursor.nodeIndexStack.push(index); + cursor.nodeIndexStack.push(InOrderMapCursor.INDEX_START); + cursor.nodeStack.push(subnode); + + return subnode.moveToNextInorder(cursor); + } + } + + // nothing found + cursor.nodeStack.pop(); + cursor.nodeIndexStack.pop(); + if (!cursor.nodeStack.isEmpty()) { + Node supernode = cursor.nodeStack.peek(); + return supernode.moveToNextInorder(cursor); + } else { + cursor.key = null; + cursor.value = null; + return false; + } + } + @Override public void prettyPrint(StringBuilder builder, int depth, int code) { builder.append("\t".repeat(Math.max(0, depth))); @@ -348,8 +391,8 @@ public class ImmutableNode extends Node { var mutableSubnode = (Node) mutable.content[i * 2 + 1]; if (mutableSubnode != null) { if (datas * 2 + nodes + 1 <= immutableLength) { - Object immutableSubnode = immutable.content[immutableLength - 1 - nodes]; - if (!mutableSubnode.equals(immutableSubnode)) { + Object immutableSubNode = immutable.content[immutableLength - 1 - nodes]; + if (!mutableSubnode.equals(immutableSubNode)) { return false; } nodes++; @@ -359,6 +402,7 @@ public class ImmutableNode extends Node { } } } - return true; + + return datas * 2 + nodes == immutable.content.length; } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java new file mode 100644 index 00000000..c559d9ad --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java @@ -0,0 +1,141 @@ +package tools.refinery.store.map.internal; + +import tools.refinery.store.map.AnyVersionedMap; +import tools.refinery.store.map.ContentHashCode; +import tools.refinery.store.map.Cursor; +import tools.refinery.store.map.VersionedMap; + +import java.util.*; + +public class InOrderMapCursor implements Cursor { + // Constants + static final int INDEX_START = -1; + + // Tree stack + ArrayDeque> nodeStack; + ArrayDeque nodeIndexStack; + + + // Values + K key; + V value; + + // Hash code for checking concurrent modifications + final VersionedMap map; + final int creationHash; + + public InOrderMapCursor(VersionedMapImpl map) { + // Initializing tree stack + super(); + this.nodeStack = new ArrayDeque<>(); + this.nodeIndexStack = new ArrayDeque<>(); + if (map.root != null) { + this.nodeStack.add(map.root); + this.nodeIndexStack.push(INDEX_START); + } + + // Initializing cache + this.key = null; + this.value = null; + + // Initializing state + this.map = map; + this.creationHash = map.contentHashCode(ContentHashCode.APPROXIMATE_FAST); + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public boolean isTerminated() { + return this.nodeStack.isEmpty(); + } + + public boolean move() { + if (isDirty()) { + throw new ConcurrentModificationException(); + } + if (!isTerminated()) { + var node = this.nodeStack.peek(); + if (node == null) { + throw new IllegalStateException("Cursor is not terminated but the current node is missing"); + } + boolean result = node.moveToNextInorder(this); + if (this.nodeIndexStack.size() != this.nodeStack.size()) { + throw new IllegalArgumentException("Node stack is corrupted by illegal moves!"); + } + return result; + } + return false; + } + + public boolean skipCurrentNode() { + nodeStack.pop(); + nodeIndexStack.pop(); + return move(); + } + + @Override + public boolean isDirty() { + return this.map.contentHashCode(ContentHashCode.APPROXIMATE_FAST) != this.creationHash; + } + + @Override + public Set getDependingMaps() { + return Set.of(this.map); + } + + public static boolean sameSubNode(InOrderMapCursor cursor1, InOrderMapCursor cursor2) { + Node nodeOfCursor1 = cursor1.nodeStack.peek(); + Node nodeOfCursor2 = cursor2.nodeStack.peek(); + return Objects.equals(nodeOfCursor1, nodeOfCursor2); + } + + /** + * Compares the state of two cursors started on two {@link VersionedMap} of the same + * {@link tools.refinery.store.map.VersionedMapStore}. + * @param Key type + * @param Value type + * @param cursor1 first cursor + * @param cursor2 second cursor + * @return Positive number if cursor 1 is behind, negative number if cursor 2 is behind, and 0 if they are at the + * same position. + */ + public static int comparePosition(InOrderMapCursor cursor1, InOrderMapCursor cursor2) { + // If the state does not determine the order, then compare @nodeIndexStack. + Iterator nodeIndexStack1 = cursor1.nodeIndexStack.descendingIterator(); + Iterator nodeIndexStack2 = cursor2.nodeIndexStack.descendingIterator(); + + while(nodeIndexStack1.hasNext() && nodeIndexStack2.hasNext()){ + final int index1 = nodeIndexStack1.next(); + final int index2 = nodeIndexStack2.next(); + if(index1 < index2) { + return 1; + } else if(index1 > index2) { + return -1; + } + } + + return 0; + } + + /** + * Compares the depth of two cursors started on @{@link VersionedMap} of the same + * {@link tools.refinery.store.map.VersionedMapStore}. + * @param Key type + * @param Value type + * @param cursor1 first cursor + * @param cursor2 second cursor + * @return Positive number if cursor 1 is deeper, negative number if cursor 2 is deeper, and 0 if they are at the + * same depth. + */ + public static int compareDepth(InOrderMapCursor cursor1, InOrderMapCursor cursor2) { + int d1 = cursor1.nodeIndexStack.size(); + int d2 = cursor2.nodeIndexStack.size(); + return Integer.compare(d1, d2); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java index 50fcfcd3..7e4f82e8 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java @@ -7,7 +7,6 @@ import tools.refinery.store.map.VersionedMap; import java.util.ArrayDeque; import java.util.ConcurrentModificationException; -import java.util.Iterator; import java.util.Set; public class MapCursor implements Cursor { @@ -79,13 +78,6 @@ public class MapCursor implements Cursor { return false; } - public boolean skipCurrentNode() { - nodeStack.pop(); - nodeIndexStack.pop(); - dataIndex = INDEX_FINISH; - return move(); - } - @Override public boolean isDirty() { return this.map.contentHashCode(ContentHashCode.APPROXIMATE_FAST) != this.creationHash; @@ -95,55 +87,4 @@ public class MapCursor implements Cursor { public Set getDependingMaps() { return Set.of(this.map); } - - public static boolean sameSubNode(MapCursor cursor1, MapCursor cursor2) { - Node nodeOfCursor1 = cursor1.nodeStack.peek(); - Node nodeOfCursor2 = cursor2.nodeStack.peek(); - if (nodeOfCursor1 != null && nodeOfCursor2 != null) { - return nodeOfCursor1.equals(nodeOfCursor2); - } else { - return false; - } - } - - /** - * Compares the state of two cursors started on two @{@link VersionedMap of the }same - * {@link tools.refinery.store.map.VersionedMapStore}. - * @param Key type - * @param Value type - * @param cursor1 first cursor - * @param cursor2 second cursor - * @return Positive number if cursor 1 is behind, negative number if cursor 2 is behind, and 0 if they are at the - * same position. - */ - public static int compare(MapCursor cursor1, MapCursor cursor2) { - // Checking the state of the cursors - if(!cursor1.isTerminated() && cursor2.isTerminated()) return -1; - else if(cursor1.isTerminated() && !cursor2.isTerminated()) return 1; - else if(cursor1.isTerminated() && cursor2.isTerminated()) return 0; - - // If the state does not determine the order, then compare @nodeIndexStack. - Iterator stack1 = cursor1.nodeIndexStack.descendingIterator(); - Iterator stack2 = cursor2.nodeIndexStack.descendingIterator(); - if (stack1.hasNext()) { - if (!stack2.hasNext()) { - // stack 2 has no more element, thus stack 1 is deeper - return 1; - } - int val1 = stack1.next(); - int val2 = stack2.next(); - if (val1 < val2) { - return -1; - } else if (val2 < val1) { - return 1; - } - } - if (stack2.hasNext()) { - // stack 2 has more element, thus stack 2 is deeper - return 1; - } - - // two cursors are equally deep decide by data index - return Integer.compare(cursor1.dataIndex, cursor2.dataIndex); - } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java index 6c076ce5..59e8d738 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java @@ -1,7 +1,6 @@ package tools.refinery.store.map.internal; import tools.refinery.store.map.AnyVersionedMap; -import tools.refinery.store.map.ContinousHashProvider; import tools.refinery.store.map.Cursor; import tools.refinery.store.map.DiffCursor; @@ -14,37 +13,78 @@ import java.util.stream.Stream; * A cursor representing the difference between two states of a map. * * @author Oszkar Semerath - * */ public class MapDiffCursor implements DiffCursor, Cursor { + private enum State { + /** + * initialized state. + */ + INIT, + /** + * Unstable state. + */ + MOVING_MOVING_SAME_KEY_SAME_VALUE, + /** + * Both cursors are moving, and they are on the same sub-node. + */ + MOVING_MOVING_SAME_NODE, + /** + * Both cursors are moving, cursor 1 is behind. + */ + MOVING_MOVING_BEHIND1, + /** + * Both cursors are moving, cursor 2 is behind. + */ + MOVING_MOVING_BEHIND2, + /** + * Both cursors are moving, cursor 1 is on the same key as cursor 2, values are different + */ + MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE, + /** + * Cursor 1 is moving, Cursor 2 is terminated. + */ + MOVING_TERMINATED, + /** + * Cursor 1 is terminated , Cursor 2 is moving. + */ + TERMINATED_MOVING, + /** + * Both cursors are terminated. + */ + TERMINATED_TERMINATED, + /** + * Both Cursors are moving, and they are on an incomparable position. + * It is resolved by showing Cursor 1. + */ + MOVING_MOVING_HASH1, + /** + * Both Cursors are moving, and they are on an incomparable position. + * It is resolved by showing Cursor 2. + */ + MOVING_MOVING_HASH2 + } + /** * Default nodeId representing missing elements. */ private final V defaultValue; - private final MapCursor cursor1; - private final MapCursor cursor2; - private final ContinousHashProvider hashProvider; + private final InOrderMapCursor cursor1; + private final InOrderMapCursor cursor2; + + // State + State state = State.INIT; // Values private K key; private V fromValue; private V toValue; - // State - /** - * Positive number if cursor 1 is behind, negative number if cursor 2 is behind, - * and 0 if they are at the same position. - */ - private int cursorRelation; - private HashClash hashClash = HashClash.NONE; - public MapDiffCursor(ContinousHashProvider hashProvider, V defaultValue, Cursor cursor1, - Cursor cursor2) { + public MapDiffCursor(V defaultValue, InOrderMapCursor cursor1, InOrderMapCursor cursor2) { super(); - this.hashProvider = hashProvider; this.defaultValue = defaultValue; - this.cursor1 = (MapCursor) cursor1; - this.cursor2 = (MapCursor) cursor2; + this.cursor1 = cursor1; + this.cursor2 = cursor2; } @Override @@ -68,7 +108,7 @@ public class MapDiffCursor implements DiffCursor, Cursor { } public boolean isTerminated() { - return cursor1.isTerminated() && cursor2.isTerminated(); + return this.state == State.TERMINATED_TERMINATED; } @Override @@ -78,148 +118,142 @@ public class MapDiffCursor implements DiffCursor, Cursor { @Override public Set getDependingMaps() { - return Stream.concat(cursor1.getDependingMaps().stream(), cursor2.getDependingMaps().stream()) - .map(AnyVersionedMap.class::cast) - .collect(Collectors.toUnmodifiableSet()); - } - - protected void updateState() { - if (!isTerminated()) { - this.cursorRelation = MapCursor.compare(cursor1, cursor2); - if (cursorRelation > 0 || cursor2.isTerminated()) { - this.key = cursor1.getKey(); - this.fromValue = cursor1.getValue(); - this.toValue = defaultValue; - } else if (cursorRelation < 0 || cursor1.isTerminated()) { - this.key = cursor2.getKey(); - this.fromValue = defaultValue; - this.toValue = cursor1.getValue(); - } else { - // cursor1 = cursor2 - if (cursor1.getKey().equals(cursor2.getKey())) { - this.key = cursor1.getKey(); - this.fromValue = cursor1.getValue(); - this.toValue = defaultValue; - } else { - resolveHashClashWithFirstEntry(); - } - } - } + return Stream.concat(cursor1.getDependingMaps().stream(), cursor2.getDependingMaps().stream()).map(AnyVersionedMap.class::cast).collect(Collectors.toUnmodifiableSet()); } - protected void resolveHashClashWithFirstEntry() { - int compareResult = this.hashProvider.compare(cursor1.key, cursor2.key); - if (compareResult < 0) { - this.hashClash = HashClash.STUCK_CURSOR_2; - this.cursorRelation = 0; - this.key = cursor1.key; - this.fromValue = cursor1.value; - this.toValue = defaultValue; - } else if (compareResult > 0) { - this.hashClash = HashClash.STUCK_CURSOR_1; - this.cursorRelation = 0; - this.key = cursor2.key; - this.fromValue = defaultValue; - this.toValue = cursor2.value; - } else { - throw new IllegalArgumentException("Inconsistent compare result for diffCursor"); - } + private boolean isInStableState() { + return this.state != State.MOVING_MOVING_SAME_KEY_SAME_VALUE + && this.state != State.MOVING_MOVING_SAME_NODE && this.state != State.INIT; } - protected boolean isInHashClash() { - return this.hashClash != HashClash.NONE; + private boolean updateAndReturnWithResult() { + return switch (this.state) { + case INIT -> throw new IllegalStateException("DiffCursor terminated without starting!"); + case MOVING_MOVING_SAME_KEY_SAME_VALUE, MOVING_MOVING_SAME_NODE -> + throw new IllegalStateException("DiffCursor terminated in unstable state!"); + case MOVING_MOVING_BEHIND1, MOVING_TERMINATED, MOVING_MOVING_HASH1 -> { + this.key = this.cursor1.getKey(); + this.fromValue = this.cursor1.getValue(); + this.toValue = this.defaultValue; + yield true; + } + case MOVING_MOVING_BEHIND2, TERMINATED_MOVING, MOVING_MOVING_HASH2 -> { + this.key = this.cursor2.getKey(); + this.fromValue = this.defaultValue; + this.toValue = cursor2.getValue(); + yield true; + } + case MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE -> { + this.key = this.cursor1.getKey(); + this.fromValue = this.cursor1.getValue(); + this.toValue = this.cursor2.getValue(); + yield true; + } + case TERMINATED_TERMINATED -> { + this.key = null; + this.fromValue = null; + this.toValue = null; + yield false; + } + }; } - protected void resolveHashClashWithSecondEntry() { - switch (this.hashClash) { - case STUCK_CURSOR_1 -> { - this.hashClash = HashClash.NONE; - this.cursorRelation = 0; - this.key = cursor1.key; - this.fromValue = cursor1.value; - this.toValue = defaultValue; - } - case STUCK_CURSOR_2 -> { - this.hashClash = HashClash.NONE; - this.cursorRelation = 0; - this.key = cursor2.key; - this.fromValue = defaultValue; - this.toValue = cursor2.value; - } - default -> throw new IllegalArgumentException("Inconsistent compare result for diffCursor"); - } + public boolean move() { + do { + this.state = moveOne(this.state); + } while (!isInStableState()); + return updateAndReturnWithResult(); } - /** - * Checks if two states has the same values, i.e., there is no difference. - * @return whether two states has the same values - */ - protected boolean sameValues() { - if(cursor1.isTerminated() || cursor2.isTerminated()) return false; - else return Objects.equals(this.fromValue, this.toValue); + private State moveOne(State currentState) { + return switch (currentState) { + case INIT, MOVING_MOVING_HASH2, MOVING_MOVING_SAME_KEY_SAME_VALUE, MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE -> { + boolean cursor1Moved = cursor1.move(); + boolean cursor2Moved = cursor2.move(); + yield recalculateStateAfterCursorMovement(cursor1Moved, cursor2Moved); + } + case MOVING_MOVING_SAME_NODE -> { + boolean cursor1Moved = cursor1.skipCurrentNode(); + boolean cursor2Moved = cursor2.skipCurrentNode(); + yield recalculateStateAfterCursorMovement(cursor1Moved, cursor2Moved); + } + case MOVING_MOVING_BEHIND1 -> { + boolean cursorMoved = cursor1.move(); + if (cursorMoved) { + yield recalculateStateBasedOnCursorRelation(); + } else { + yield State.TERMINATED_MOVING; + } + } + case MOVING_MOVING_BEHIND2 -> { + boolean cursorMoved = cursor2.move(); + if (cursorMoved) { + yield recalculateStateBasedOnCursorRelation(); + } else { + yield State.MOVING_TERMINATED; + } + } + case TERMINATED_MOVING -> { + boolean cursorMoved = cursor2.move(); + if (cursorMoved) { + yield State.TERMINATED_MOVING; + } else { + yield State.TERMINATED_TERMINATED; + } + } + case MOVING_TERMINATED -> { + boolean cursorMoved = cursor1.move(); + if (cursorMoved) { + yield State.MOVING_TERMINATED; + } else { + yield State.TERMINATED_TERMINATED; + } + } + case MOVING_MOVING_HASH1 -> State.MOVING_MOVING_HASH2; + case TERMINATED_TERMINATED -> throw new IllegalStateException("Trying to move while terminated!"); + }; } - protected boolean moveOne() { - if (isTerminated()) { - return false; - } - if (this.cursorRelation > 0 || cursor2.isTerminated()) { - return cursor1.move(); - } else if (this.cursorRelation < 0 || cursor1.isTerminated()) { - return cursor2.move(); + private State recalculateStateAfterCursorMovement(boolean cursor1Moved, boolean cursor2Moved) { + if (cursor1Moved && cursor2Moved) { + return recalculateStateBasedOnCursorRelation(); + } else if (cursor1Moved) { + return State.MOVING_TERMINATED; + } else if (cursor2Moved) { + return State.TERMINATED_MOVING; } else { - boolean moved1 = cursor1.move(); - boolean moved2 = cursor2.move(); - return moved1 && moved2; + return State.TERMINATED_TERMINATED; } } - private boolean skipNode() { - if (isTerminated()) { - throw new IllegalStateException("DiffCursor tries to skip when terminated!"); - } - boolean update1 = cursor1.skipCurrentNode(); - boolean update2 = cursor2.skipCurrentNode(); - updateState(); - return update1 && update2; - } + private State recalculateStateBasedOnCursorRelation() { + final int relation = InOrderMapCursor.comparePosition(cursor1, cursor2); - protected boolean moveToConsistentState() { - if (!isTerminated()) { - boolean changed; - boolean lastResult = true; - do { - changed = false; - if (MapCursor.sameSubNode(cursor1, cursor2)) { - lastResult = skipNode(); - changed = true; - } - if (sameValues()) { - lastResult = moveOne(); - changed = true; - } - updateState(); - } while (changed && !isTerminated()); - return lastResult; - } else { - return false; + if (relation > 0) { + return State.MOVING_MOVING_BEHIND1; + } else if (relation < 0) { + return State.MOVING_MOVING_BEHIND2; } - } - public boolean move() { - if (!isTerminated()) { - if (isInHashClash()) { - this.resolveHashClashWithSecondEntry(); - return true; + if (InOrderMapCursor.sameSubNode(cursor1, cursor2)) { + return State.MOVING_MOVING_SAME_NODE; + } else if (Objects.equals(cursor1.getKey(), cursor2.getKey())) { + if (Objects.equals(cursor1.getValue(), cursor2.getValue())) { + return State.MOVING_MOVING_SAME_KEY_SAME_VALUE; } else { - if (moveOne()) { - return moveToConsistentState(); - } else { - return false; - } + return State.MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE; } + } + + final int depth = InOrderMapCursor.compareDepth(cursor1, cursor2); + + if (depth > 0) { + return State.MOVING_MOVING_BEHIND1; + } else if (depth < 0) { + return State.MOVING_MOVING_BEHIND2; + } else { + return State.MOVING_MOVING_HASH1; + } - } else - return false; } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java index cdc66a10..81bf6188 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java @@ -39,12 +39,12 @@ public class MutableNode extends Node { int dataUsed = 0; int nodeUsed = 0; for (int i = 0; i < FACTOR; i++) { - int bitposition = 1 << i; - if ((node.dataMap & bitposition) != 0) { + int bitPosition = 1 << i; + if ((node.dataMap & bitPosition) != 0) { content[2 * i] = node.content[dataUsed * 2]; content[2 * i + 1] = node.content[dataUsed * 2 + 1]; dataUsed++; - } else if ((node.nodeMap & bitposition) != 0) { + } else if ((node.nodeMap & bitPosition) != 0) { content[2 * i + 1] = node.content[node.content.length - 1 - nodeUsed]; nodeUsed++; } @@ -80,7 +80,7 @@ public class MutableNode extends Node { int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); @SuppressWarnings("unchecked") K keyCandidate = (K) content[2 * selectedHashFragment]; if (keyCandidate != null) { - // If has key + // If it has key if (keyCandidate.equals(key)) { // The key is equals to an existing key -> update entry if (value == defaultValue) { @@ -101,24 +101,22 @@ public class MutableNode extends Node { return moveDownAndSplit(hashProvider, key, value, keyCandidate, hash, depth, selectedHashFragment); } } + } + // If it does not have key, check for value + @SuppressWarnings("unchecked") var nodeCandidate = (Node) content[2 * selectedHashFragment + 1]; + if (nodeCandidate != null) { + // If it has value, it is a sub-node -> update that + int newDepth = incrementDepth(depth); + var newNode = nodeCandidate.putValue(key, value, oldValueBox, hashProvider, defaultValue, newHash(hashProvider, key, hash, newDepth), newDepth); + return updateWithSubNode(selectedHashFragment, newNode, (value == null && defaultValue == null) || (value != null && value.equals(defaultValue))); } else { - // If it does not have key, check for value - @SuppressWarnings("unchecked") var nodeCandidate = (Node) content[2 * selectedHashFragment + 1]; - if (nodeCandidate != null) { - // If it has value, it is a subnode -> upate that - int newDepth = incrementDepth(depth); - var newNode = nodeCandidate.putValue(key, value, oldValueBox, hashProvider, defaultValue, newHash(hashProvider, key, hash, newDepth), newDepth); - return updateWithSubNode(selectedHashFragment, newNode, (value == null && defaultValue == null) || (value != null && value.equals(defaultValue))); + // If it does not have value, put it in the empty place + if (value == defaultValue) { + // don't need to add new key-value pair + oldValueBox.setOldValue(defaultValue); + return this; } else { - // If it does not have value, put it in the empty place - if (value == defaultValue) { - // dont need to add new key-value pair - oldValueBox.setOldValue(defaultValue); - return this; - } else { - return addEntry(key, value, oldValueBox, selectedHashFragment, defaultValue); - } - + return addEntry(key, value, oldValueBox, selectedHashFragment, defaultValue); } } } @@ -170,7 +168,7 @@ public class MutableNode extends Node { if (immutableNewNode != null) { int orphaned = immutableNewNode.isOrphaned(); if (orphaned >= 0) { - // orphan subnode data is replaced with data + // orphan sub-node data is replaced with data content[2 * selectedHashFragment] = immutableNewNode.content[orphaned * 2]; content[2 * selectedHashFragment + 1] = immutableNewNode.content[orphaned * 2 + 1]; invalidateHash(); @@ -227,11 +225,11 @@ public class MutableNode extends Node { // Pass everything as parameters for performance. @SuppressWarnings("squid:S107") - private MutableNode newNodeWithTwoEntries(ContinousHashProvider hashProvider, K key1, V value1, int oldHash1, K key2, V value2, int oldHash2, int newdepth) { - int newHash1 = newHash(hashProvider, key1, oldHash1, newdepth); - int newHash2 = newHash(hashProvider, key2, oldHash2, newdepth); - int newFragment1 = hashFragment(newHash1, shiftDepth(newdepth)); - int newFragment2 = hashFragment(newHash2, shiftDepth(newdepth)); + private MutableNode newNodeWithTwoEntries(ContinousHashProvider hashProvider, K key1, V value1, int oldHash1, K key2, V value2, int oldHash2, int newDepth) { + int newHash1 = newHash(hashProvider, key1, oldHash1, newDepth); + int newHash2 = newHash(hashProvider, key2, oldHash2, newDepth); + int newFragment1 = hashFragment(newHash1, shiftDepth(newDepth)); + int newFragment2 = hashFragment(newHash2, shiftDepth(newDepth)); MutableNode subNode = new MutableNode<>(); if (newFragment1 != newFragment2) { @@ -241,7 +239,7 @@ public class MutableNode extends Node { subNode.content[newFragment2 * 2] = key2; subNode.content[newFragment2 * 2 + 1] = value2; } else { - MutableNode subSubNode = newNodeWithTwoEntries(hashProvider, key1, value1, newHash1, key2, value2, newHash2, incrementDepth(newdepth)); + MutableNode subSubNode = newNodeWithTwoEntries(hashProvider, key1, value1, newHash1, key2, value2, newHash2, incrementDepth(newDepth)); subNode.content[newFragment1 * 2 + 1] = subSubNode; } subNode.invalidateHash(); @@ -305,13 +303,13 @@ public class MutableNode extends Node { cursor.dataIndex = MapCursor.INDEX_FINISH; } - // 2. look inside the subnodes + // 2. look inside the sub-nodes if(cursor.nodeIndexStack.peek()==null) { throw new IllegalStateException("Cursor moved to the next state when the state is empty."); } for (int index = cursor.nodeIndexStack.peek() + 1; index < FACTOR; index++) { if (this.content[index * 2] == null && this.content[index * 2 + 1] != null) { - // 2.1 found next subnode, move down to the subnode + // 2.1 found next sub-node, move down to the sub-node Node subnode = (Node) this.content[index * 2 + 1]; cursor.dataIndex = MapCursor.INDEX_START; @@ -323,7 +321,7 @@ public class MutableNode extends Node { return subnode.moveToNext(cursor); } } - // 3. no subnode found, move up + // 3. no sub-node found, move up cursor.nodeStack.pop(); cursor.nodeIndexStack.pop(); if (!cursor.nodeStack.isEmpty()) { @@ -336,6 +334,49 @@ public class MutableNode extends Node { } } + @Override + @SuppressWarnings("unchecked") + boolean moveToNextInorder(InOrderMapCursor cursor) { + if(cursor.nodeIndexStack.peek()==null || cursor.nodeStack.peek()==null) { + throw new IllegalStateException("Cursor moved to the next state when the state is empty."); + } + + int position = cursor.nodeIndexStack.peek(); + + for (int index = position + 1; index < FACTOR; index++) { + // data found + if (this.content[index * 2] != null) { + cursor.nodeIndexStack.pop(); + cursor.nodeIndexStack.push(index); + + cursor.key = (K) this.content[index * 2]; + cursor.value = (V) this.content[index * 2 + 1]; + return true; + } else if (this.content[index * 2 +1] != null) { + // sub-node found + Node subnode = (Node) this.content[index * 2 +1]; + cursor.nodeIndexStack.pop(); + cursor.nodeIndexStack.push(index); + cursor.nodeIndexStack.push(InOrderMapCursor.INDEX_START); + cursor.nodeStack.push(subnode); + + return subnode.moveToNextInorder(cursor); + } + } + + // nothing found + cursor.nodeStack.pop(); + cursor.nodeIndexStack.pop(); + if (!cursor.nodeStack.isEmpty()) { + Node supernode = cursor.nodeStack.peek(); + return supernode.moveToNextInorder(cursor); + } else { + cursor.key = null; + cursor.value = null; + return false; + } + } + @Override public void prettyPrint(StringBuilder builder, int depth, int code) { builder.append("\t".repeat(Math.max(0, depth))); @@ -361,7 +402,7 @@ public class MutableNode extends Node { } } builder.append(")"); - // print subnodes + // print sub-nodes for (int i = 0; i < FACTOR; i++) { if (content[2 * i] == null && content[2 * i + 1] != null) { @SuppressWarnings("unchecked") Node subNode = (Node) content[2 * i + 1]; @@ -397,7 +438,7 @@ public class MutableNode extends Node { } } } - // check subnodes + // check sub-nodes for (int i = 0; i < FACTOR; i++) { if (this.content[2 * i + 1] != null && this.content[2 * i] == null) { @SuppressWarnings("unchecked") var subNode = (Node) this.content[2 * i + 1]; @@ -421,13 +462,11 @@ public class MutableNode extends Node { @Override public int hashCode() { - if (this.cachedHashValid) { - return this.cachedHash; - } else { + if (!this.cachedHashValid) { this.cachedHash = Arrays.hashCode(content); this.cachedHashValid = true; - return this.cachedHash; } + return this.cachedHash; } @Override diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java index c49be280..3dd332da 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java @@ -108,10 +108,12 @@ public abstract class Node { * @return Whether there was a next value to move on. */ abstract boolean moveToNext(MapCursor cursor); + abstract boolean moveToNextInorder(InOrderMapCursor cursor); ///////// FOR printing public abstract void prettyPrint(StringBuilder builder, int depth, int code); + @Override public String toString() { StringBuilder stringBuilder = new StringBuilder(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java index fb359431..2ceca463 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java @@ -75,7 +75,9 @@ public class VersionedMapImpl implements VersionedMap { Iterator keyIterator = keys.iterator(); Iterator valueIterator = values.iterator(); while (keyIterator.hasNext()) { - this.put(keyIterator.next(), valueIterator.next()); + var key = keyIterator.next(); + var value = valueIterator.next(); + this.put(key,value); } } else { while (cursor.move()) { @@ -109,12 +111,10 @@ public class VersionedMapImpl implements VersionedMap { @Override public DiffCursor getDiffCursor(long toVersion) { - - Cursor fromCursor = this.getAll(); - VersionedMap toMap = this.store.createMap(toVersion); - Cursor toCursor = toMap.getAll(); - return new MapDiffCursor<>(this.hashProvider, this.defaultValue, fromCursor, toCursor); - + InOrderMapCursor fromCursor = new InOrderMapCursor<>(this); + VersionedMapImpl toMap = (VersionedMapImpl) this.store.createMap(toVersion); + InOrderMapCursor toCursor = new InOrderMapCursor<>(toMap); + return new MapDiffCursor<>(this.defaultValue, fromCursor, toCursor); } @@ -152,7 +152,11 @@ public class VersionedMapImpl implements VersionedMap { @Override public int contentHashCode(ContentHashCode mode) { // Calculating the root hashCode is always fast, because {@link Node} caches its hashCode. - return Objects.hashCode(root); + if(root == null) { + return 0; + } else { + return root.hashCode(); + } } @Override 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 6d82f5d7..c850d334 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 @@ -4,11 +4,9 @@ import tools.refinery.store.map.Cursor; import tools.refinery.store.map.DiffCursor; import tools.refinery.store.map.VersionedMap; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.internal.MapDiffCursor; import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.InterpretationListener; import tools.refinery.store.model.Model; -import tools.refinery.store.model.TupleHashProvider; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; @@ -19,16 +17,13 @@ import java.util.List; public class VersionedInterpretation implements Interpretation { private final ModelImpl model; private final Symbol symbol; - private final VersionedMapStore store; private final VersionedMap map; private final List> listeners = new ArrayList<>(); private final List> restoreListeners = new ArrayList<>(); - private VersionedInterpretation(ModelImpl model, Symbol symbol, VersionedMapStore store, - VersionedMap map) { + private VersionedInterpretation(ModelImpl model, Symbol symbol, VersionedMap map) { this.model = model; this.symbol = symbol; - this.store = store; this.map = map; } @@ -111,9 +106,7 @@ public class VersionedInterpretation implements Interpretation { @Override public DiffCursor getDiffCursor(long to) { - var fromCursor = getAll(); - var toCursor = store.createMap(to).getAll(); - return new MapDiffCursor<>(TupleHashProvider.INSTANCE, symbol.defaultValue(), fromCursor, toCursor); + return map.getDiffCursor(to); } public long commit() { @@ -148,7 +141,7 @@ public class VersionedInterpretation implements Interpretation { @SuppressWarnings("unchecked") var typedSymbol = (Symbol) symbol; var map = store.createMap(); - return new VersionedInterpretation<>(model, typedSymbol, store, map); + return new VersionedInterpretation<>(model, typedSymbol, map); } static VersionedInterpretation of(ModelImpl model, AnySymbol symbol, VersionedMapStore store, @@ -156,6 +149,6 @@ public class VersionedInterpretation implements Interpretation { @SuppressWarnings("unchecked") var typedSymbol = (Symbol) symbol; var map = store.createMap(state); - return new VersionedInterpretation<>(model, typedSymbol, store, map); + return new VersionedInterpretation<>(model, typedSymbol, map); } } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java new file mode 100644 index 00000000..05cf5a74 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java @@ -0,0 +1,49 @@ +package tools.refinery.store.map.tests; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.map.VersionedMapStoreBuilder; +import tools.refinery.store.map.internal.InOrderMapCursor; +import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.tests.utils.MapTestEnvironment; + +import static org.junit.jupiter.api.Assertions.*; + +class InOrderCursorTest { + @Test + void testCursor() { + var store = VersionedMapStoreBuilder.builder() + .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) + .setStateBasedImmutableWhenCommitting(true) + .setHashProvider(MapTestEnvironment.prepareHashProvider(false)) + .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy.SHARED_NODE_CACHE) + .setDefaultValue("x") + .buildOne(); + + VersionedMapImpl map = (VersionedMapImpl) store.createMap(); + checkMove(map,0); + + map.put(1,"A"); + map.commit(); + checkMove(map,1); + + + map.put(2,"B"); + map.commit(); + checkMove(map,2); + + map.put(3,"C"); + map.commit(); + checkMove(map,3); + + } + + private void checkMove(VersionedMapImpl map, int num) { + InOrderMapCursor cursor = new InOrderMapCursor<>(map); + for(int i=0; i builder) { + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, + int commitFrequency, boolean commitBeforeDiffCursor, + VersionedMapStoreBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); - iterativeRandomPutsAndCommitsThenDiffCursor(scenario, store, steps, maxKey, values, seed, commitFrequency); + iterativeRandomPutsAndCommitsThenDiffCursor(scenario, store, steps, maxKey, values, seed, commitFrequency, + commitBeforeDiffCursor); } private void iterativeRandomPutsAndCommitsThenDiffCursor(String scenario, VersionedMapStore store, - int steps, int maxKey, String[] values, int seed, int commitFrequency) { - // 1. build a map with versions - Random r = new Random(seed); - VersionedMap versioned = store.createMap(); + int steps, int maxKey, String[] values, int seed, + int commitFrequency, boolean commitBeforeDiffCursor) { + int largestCommit = -1; - for (int i = 0; i < steps; i++) { - int index = i + 1; - int nextKey = r.nextInt(maxKey); - String nextValue = values[r.nextInt(values.length)]; - try { - versioned.put(nextKey, nextValue); - } catch (Exception exception) { - exception.printStackTrace(); - fail(scenario + ":" + index + ": exception happened: " + exception); - } - if (index % commitFrequency == 0) { - long version = versioned.commit(); - largestCommit = (int) version; - } - if (index % 10000 == 0) - System.out.println(scenario + ":" + index + "/" + steps + " building finished"); - } - // 2. create a non-versioned map, - VersionedMap moving = store.createMap(); - Random r2 = new Random(seed + 1); - - final int diffTravelFrequency = commitFrequency * 2; - for (int i = 0; i < steps; i++) { - int index = i + 1; - if (index % diffTravelFrequency == 0) { - // diff-travel - long travelToVersion = r2.nextInt(largestCommit + 1); - DiffCursor diffCursor = moving.getDiffCursor(travelToVersion); - moving.putAll(diffCursor); - - } else { - // random puts - int nextKey = r2.nextInt(maxKey); - String nextValue = values[r2.nextInt(values.length)]; + { + // 1. build a map with versions + Random r = new Random(seed); + VersionedMap versioned = store.createMap(); + for (int i = 0; i < steps; i++) { + int index = i + 1; + int nextKey = r.nextInt(maxKey); + String nextValue = values[r.nextInt(values.length)]; try { - moving.put(nextKey, nextValue); + versioned.put(nextKey, nextValue); } catch (Exception exception) { exception.printStackTrace(); fail(scenario + ":" + index + ": exception happened: " + exception); } if (index % commitFrequency == 0) { - versioned.commit(); + long version = versioned.commit(); + largestCommit = (int) version; } if (index % 10000 == 0) System.out.println(scenario + ":" + index + "/" + steps + " building finished"); } } + { + // 2. create a non-versioned map, + VersionedMap moving = store.createMap(); + Random r2 = new Random(seed + 1); + + final int diffTravelFrequency = commitFrequency * 2; + for (int i = 0; i < steps; i++) { + int index = i + 1; + if (index % diffTravelFrequency == 0) { + // diff-travel + long travelToVersion = r2.nextInt(largestCommit + 1); + + VersionedMap oracle = store.createMap(travelToVersion); + + if(commitBeforeDiffCursor) { + moving.commit(); + } + DiffCursor diffCursor = moving.getDiffCursor(travelToVersion); + moving.putAll(diffCursor); + moving.commit(); + + MapTestEnvironment.compareTwoMaps(scenario + ":c" + index, oracle, moving); + + moving.restore(travelToVersion); + + } else { + // random puts + int nextKey = r2.nextInt(maxKey); + String nextValue = values[r2.nextInt(values.length)]; + try { + moving.put(nextKey, nextValue); + } catch (Exception exception) { + exception.printStackTrace(); + fail(scenario + ":" + index + ": exception happened: " + exception); + } + if (index % 10000 == 0) + System.out.println(scenario + ":" + index + "/" + steps + " building finished"); + } + } + } } public static final String title = "DiffCursor {index}/{0} Steps={1} Keys={2} Values={3} nullDefault={4} " + - "commit frequency={5} seed={6} config={7}"; + "commit frequency={5} seed={6} commit before diff={7} config={8}"; @ParameterizedTest(name = title) @MethodSource @Timeout(value = 10) @Tag("fuzz") - void parametrizedFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, VersionedMapStoreBuilder builder) { - runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, - noKeys, noValues, nullDefault, commitFrequency, builder); + void parametrizedFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, + int commitFrequency, int seed, boolean commitBeforeDiffCursor, + VersionedMapStoreBuilder builder) { + runFuzzTest("DiffCursorS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, + noKeys, noValues, nullDefault, commitFrequency, commitBeforeDiffCursor, builder); } static Stream parametrizedFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[]{100}, keyCounts, valueCounts, nullDefaultOptions, - commitFrequencyOptions, randomSeedOptions, storeConfigs); + return FuzzTestUtils.permutationWithSize(new Object[]{500}, keyCounts, valueCounts, nullDefaultOptions, + commitFrequencyOptions, randomSeedOptions, new Object[]{false,true}, storeConfigs); } @ParameterizedTest(name = title) @@ -104,9 +124,9 @@ class DiffCursorFuzzTest { @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, VersionedMapStoreBuilder builder) { - runFuzzTest("MutableImmutableCompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, - nullDefault, commitFrequency, builder); + int seed, boolean commitBeforeDiffCursor, VersionedMapStoreBuilder builder) { + runFuzzTest("DiffCursorS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, + nullDefault, commitFrequency, commitBeforeDiffCursor, builder); } static Stream parametrizedSlowFuzz() { diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java index fb6b28d8..b344d9b9 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java @@ -5,23 +5,25 @@ import tools.refinery.store.map.tests.utils.MapTestEnvironment; public final class FuzzTestCollections { public static final Object[] stepCounts = {FuzzTestUtils.FAST_STEP_COUNT}; - public static final Object[] keyCounts = {1, 32, 32 * 32}; + public static final Object[] keyCounts = {1 , 32, 32 * 32}; public static final Object[] valueCounts = {2, 3}; public static final Object[] nullDefaultOptions = {false, true}; - public static final Object[] commitFrequencyOptions = {10, 10, 100}; - public static final Object[] randomSeedOptions = {1/*, 2, 3*/}; + public static final Object[] commitFrequencyOptions = {1, 10, 100}; + public static final Object[] randomSeedOptions = {1}; public static final Object[] storeConfigs = { // State based VersionedMapStoreBuilder.builder() .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) .setStateBasedImmutableWhenCommitting(true) .setHashProvider(MapTestEnvironment.prepareHashProvider(false)) - .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy.SHARED_NODE_CACHE), + .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy + .SHARED_NODE_CACHE), VersionedMapStoreBuilder.builder() .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) .setStateBasedImmutableWhenCommitting(true) .setHashProvider(MapTestEnvironment.prepareHashProvider(true)) - .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy.SHARED_NODE_CACHE), + .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy + .SHARED_NODE_CACHE), VersionedMapStoreBuilder.builder() .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) .setStateBasedImmutableWhenCommitting(false) diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java index 69ae811e..0e695aaa 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java @@ -57,13 +57,15 @@ public class MapTestEnvironment { VersionedMap map2, List errors) { map1.checkIntegrity(); map2.checkIntegrity(); + + assertContentEqualsList(map1, map2, title + ": map1.contentEquals(map2)", errors); + assertContentEqualsList(map2, map1, title + ": map2.contentEquals(map1)", errors); assertEqualsList(map1.getSize(), map2.getSize(), title + ": Sizes not equal", errors); + for (var mode : ContentHashCode.values()) { assertEqualsList(map1.contentHashCode(mode), map2.contentHashCode(mode), title + ": " + mode + " hashCode check", errors); } - assertContentEqualsList(map1, map2, title + ": map1.contentEquals(map2)", errors); - assertContentEqualsList(map2, map1, title + ": map2.contentEquals(map1)", errors); } private static void assertEqualsList(Object o1, Object o2, String message, List errors) { @@ -177,7 +179,8 @@ public class MapTestEnvironment { K previous = null; Cursor cursor = versionedMap.getAll(); while (cursor.move()) { - System.out.println(cursor.getKey() + " " + ((VersionedMapImpl) versionedMap).getHashProvider().getHash(cursor.getKey(), 0)); + //System.out.println(cursor.getKey() + " " + ((VersionedMapImpl) versionedMap).getHashProvider() + // .getHash(cursor.getKey(), 0)); if (previous != null) { int comparisonResult = ((VersionedMapImpl) versionedMap).getHashProvider().compare(previous, cursor.getKey()); @@ -185,7 +188,6 @@ public class MapTestEnvironment { } previous = cursor.getKey(); } - System.out.println(); } public void printComparison() { -- cgit v1.2.3-54-g00ecf From a6dcff6293e960b420e26c57374a281467821556 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Fri, 21 Jul 2023 20:27:30 +0200 Subject: VersionedMapStoreFactoryBuilder.java is introduced, all tests are updated. VersionedMapStoreBuilder.java is removed. --- .../refinery/store/map/VersionedMapStore.java | 20 ++-- .../store/map/VersionedMapStoreBuilder.java | 130 -------------------- .../store/map/VersionedMapStoreFactory.java | 19 +++ .../store/map/VersionedMapStoreFactoryBuilder.java | 24 ++++ .../DeltaBasedVersionedMapStoreFactory.java | 34 ++++++ .../StateBasedVersionedMapStoreFactory.java | 33 ++++++ .../VersionedMapStoreFactoryBuilderImpl.java | 132 +++++++++++++++++++++ .../store/map/tests/InOrderCursorTest.java | 18 +-- .../refinery/store/map/tests/MapUnitTests.java | 9 +- .../store/map/tests/fuzz/CommitFuzzTest.java | 21 ++-- .../map/tests/fuzz/ContentEqualsFuzzTest.java | 20 ++-- .../store/map/tests/fuzz/DiffCursorFuzzTest.java | 10 +- .../store/map/tests/fuzz/MultiThreadFuzzTest.java | 29 +++-- .../store/map/tests/fuzz/MutableFuzzTest.java | 8 +- .../store/map/tests/fuzz/RestoreFuzzTest.java | 30 ++--- .../store/map/tests/fuzz/SingleThreadFuzzTest.java | 10 +- .../map/tests/fuzz/utils/FuzzTestCollections.java | 52 ++++---- 17 files changed, 357 insertions(+), 242 deletions(-) delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactory.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java index a8d7fb1a..7768287a 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java @@ -1,14 +1,20 @@ package tools.refinery.store.map; +import tools.refinery.store.map.internal.VersionedMapStoreFactoryBuilderImpl; + import java.util.Set; public interface VersionedMapStore { - - public VersionedMap createMap(); - public VersionedMap createMap(long state); - - public Set getStates(); + VersionedMap createMap(); + + VersionedMap createMap(long state); + + Set getStates(); + + DiffCursor getDiffCursor(long fromState, long toState); - public DiffCursor getDiffCursor(long fromState, long toState); -} \ No newline at end of file + static VersionedMapStoreFactoryBuilder builder() { + return new VersionedMapStoreFactoryBuilderImpl<>(); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java deleted file mode 100644 index 1a9aa0b3..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreBuilder.java +++ /dev/null @@ -1,130 +0,0 @@ -package tools.refinery.store.map; - -import java.util.ArrayList; -import java.util.List; - -public class VersionedMapStoreBuilder { - public enum StoreStrategy { - STATE, DELTA - } - - public enum DeltaStorageStrategy { - LIST, SET - } - - public enum StateStorageStrategy { - NO_NODE_CACHE, SHARED_NODE_CACHE, SHARED_NODE_CACHE_IN_GROUP - } - - public static VersionedMapStoreBuilder builder() { - return new VersionedMapStoreBuilder<>(); - } - protected VersionedMapStoreBuilder() { - } - protected VersionedMapStoreBuilder(VersionedMapStoreBuilder other) { - this.defaultValue = other.defaultValue; - this.defaultSet = other.defaultSet; - this.strategy = other.strategy; - this.stateBasedImmutableWhenCommitting = other.stateBasedImmutableWhenCommitting; - this.stateBasedNodeSharingStrategy = other.stateBasedNodeSharingStrategy; - this.hashProvider = other.hashProvider; - this.deltaStorageStrategy = other.deltaStorageStrategy; - } - protected boolean defaultSet = false; - protected V defaultValue = null; - protected StoreStrategy strategy = StoreStrategy.DELTA; - protected Boolean stateBasedImmutableWhenCommitting = false; - protected StateStorageStrategy stateBasedNodeSharingStrategy = StateStorageStrategy.SHARED_NODE_CACHE_IN_GROUP; - protected ContinousHashProvider hashProvider = null; - protected DeltaStorageStrategy deltaStorageStrategy = DeltaStorageStrategy.LIST; - - public VersionedMapStoreBuilder setDefaultValue(V defaultValue) { - var result = new VersionedMapStoreBuilder<>(this); - result.defaultValue = defaultValue; - result.defaultSet = true; - return result; - } - - public VersionedMapStoreBuilder setStrategy(StoreStrategy strategy) { - var result = new VersionedMapStoreBuilder<>(this); - result.strategy = strategy; - return result; - } - - public VersionedMapStoreBuilder setHashProvider(ContinousHashProvider hashProvider) { - var result = new VersionedMapStoreBuilder<>(this); - result.hashProvider = hashProvider; - return result; - } - - public VersionedMapStoreBuilder setStateBasedImmutableWhenCommitting(boolean toImmutableWhenCommitting) { - var result = new VersionedMapStoreBuilder<>(this); - result.stateBasedImmutableWhenCommitting = toImmutableWhenCommitting; - return result; - } - - public VersionedMapStoreBuilder setStateBasedNodeSharingStrategy(StateStorageStrategy strategy) { - var result = new VersionedMapStoreBuilder<>(this); - result.stateBasedNodeSharingStrategy = strategy; - return result; - } - - public VersionedMapStoreBuilder setDeltaStorageStrategy(DeltaStorageStrategy deltaStorageStrategy) { - var result = new VersionedMapStoreBuilder<>(this); - result.deltaStorageStrategy = deltaStorageStrategy; - return result; - } - - public VersionedMapStore buildOne() { - if(!defaultSet) { - throw new IllegalStateException("Default value is missing!"); - } - return switch (strategy) { - case DELTA -> new VersionedMapStoreDeltaImpl<>( - this.deltaStorageStrategy == DeltaStorageStrategy.SET, - this.defaultValue); - case STATE -> new VersionedMapStoreImpl<>( - this.hashProvider, - this.defaultValue, - new VersionedMapStoreConfiguration( - this.stateBasedImmutableWhenCommitting, - this.stateBasedNodeSharingStrategy != StateStorageStrategy.NO_NODE_CACHE, - this.stateBasedNodeSharingStrategy == StateStorageStrategy.SHARED_NODE_CACHE_IN_GROUP)); - }; - } - - public List> buildGroup(int amount) { - if(!defaultSet) { - throw new IllegalStateException("Default value is missing!"); - } - if (this.strategy == StoreStrategy.STATE && - this.stateBasedNodeSharingStrategy == StateStorageStrategy.SHARED_NODE_CACHE_IN_GROUP) { - return VersionedMapStoreImpl.createSharedVersionedMapStores( - amount, - this.hashProvider, - this.defaultValue, - new VersionedMapStoreConfiguration( - this.stateBasedImmutableWhenCommitting, - true, - true)); - } else { - List> result = new ArrayList<>(amount); - for (int i = 0; i < amount; i++) { - result.add(buildOne()); - } - return result; - } - } - - @Override - public String toString() { - return "VersionedMapStoreBuilder{" + - "defaultValue=" + defaultValue + - ", strategy=" + strategy + - ", stateBasedImmutableWhenCommitting=" + stateBasedImmutableWhenCommitting + - ", stateBasedNodeSharingStrategy=" + stateBasedNodeSharingStrategy + - ", hashProvider=" + hashProvider + - ", deltaStorageStrategy=" + deltaStorageStrategy + - '}'; - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactory.java new file mode 100644 index 00000000..5f882a3a --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactory.java @@ -0,0 +1,19 @@ +package tools.refinery.store.map; + +import java.util.List; + +public interface VersionedMapStoreFactory { + /** + * Constructs a new instance of {@link VersionedMap}. + * @return The new instance. + */ + VersionedMapStore createOne(); + + /** + * Constructs a group of {@link VersionedMap}s with the same configuration. If possible, the stores share + * resources with each other. + * @param amount The amount of new instances to be created. + * @return A list of new stores with the given number of elements. + */ + List> createGroup(int amount); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java new file mode 100644 index 00000000..9cf17b49 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java @@ -0,0 +1,24 @@ +package tools.refinery.store.map; + +public interface VersionedMapStoreFactoryBuilder { + enum StoreStrategy { + STATE, DELTA + } + + enum DeltaTransactionStrategy { + LIST, SET + } + + enum SharingStrategy { + NO_NODE_CACHE, SHARED_NODE_CACHE, SHARED_NODE_CACHE_IN_GROUP + } + + VersionedMapStoreFactoryBuilder defaultValue(V defaultValue); + VersionedMapStoreFactoryBuilder strategy(StoreStrategy strategy); + VersionedMapStoreFactoryBuilder stateBasedImmutableWhenCommitting(boolean transformToImmutable); + VersionedMapStoreFactoryBuilder stateBasedSharingStrategy(SharingStrategy sharingStrategy); + VersionedMapStoreFactoryBuilder stateBasedHashProvider(ContinousHashProvider hashProvider); + VersionedMapStoreFactoryBuilder deltaTransactionStrategy(DeltaTransactionStrategy deltaStrategy); + + VersionedMapStoreFactory build(); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java new file mode 100644 index 00000000..29ec0da1 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java @@ -0,0 +1,34 @@ +package tools.refinery.store.map.internal; + +import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.map.VersionedMapStoreDeltaImpl; +import tools.refinery.store.map.VersionedMapStoreFactory; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; + +import java.util.ArrayList; +import java.util.List; + +public class DeltaBasedVersionedMapStoreFactory implements VersionedMapStoreFactory { + private final V defaultValue; + private final boolean summarizeChanges; + + public DeltaBasedVersionedMapStoreFactory(V defaultValue, + VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy deltaTransactionStrategy) { + this.defaultValue = defaultValue; + this.summarizeChanges = deltaTransactionStrategy == VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy.SET; + } + + @Override + public VersionedMapStore createOne() { + return new VersionedMapStoreDeltaImpl<>(summarizeChanges, defaultValue); + } + + @Override + public List> createGroup(int amount) { + List> result = new ArrayList<>(amount); + for(int i=0; i(summarizeChanges,defaultValue)); + } + return result; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java new file mode 100644 index 00000000..80dc347f --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java @@ -0,0 +1,33 @@ +package tools.refinery.store.map.internal; + +import tools.refinery.store.map.*; + +import java.util.List; + +public class StateBasedVersionedMapStoreFactory implements VersionedMapStoreFactory { + private final V defaultValue; + private final ContinousHashProvider continousHashProvider; + private final VersionedMapStoreConfiguration config; + + public StateBasedVersionedMapStoreFactory(V defaultValue, Boolean transformToImmutable, VersionedMapStoreFactoryBuilder.SharingStrategy sharingStrategy, ContinousHashProvider continousHashProvider) { + this.defaultValue = defaultValue; + this.continousHashProvider = continousHashProvider; + + this.config = new VersionedMapStoreConfiguration( + transformToImmutable, + sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE || sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP, + sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP); + } + + @Override + public VersionedMapStore createOne() { + return new VersionedMapStoreImpl<>(continousHashProvider, defaultValue, config); + + } + + @Override + public List> createGroup(int amount) { + return VersionedMapStoreImpl.createSharedVersionedMapStores(amount, continousHashProvider, defaultValue, + config); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java new file mode 100644 index 00000000..3719eef5 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java @@ -0,0 +1,132 @@ +package tools.refinery.store.map.internal; + +import tools.refinery.store.map.ContinousHashProvider; +import tools.refinery.store.map.VersionedMapStoreFactory; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; + +public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapStoreFactoryBuilder { + + private boolean defaultSet = false; + private V defaultValue; + private StoreStrategy strategy = null; + private Boolean transformToImmutable = null; + private SharingStrategy sharingStrategy = null; + private ContinousHashProvider continousHashProvider = null; + private DeltaTransactionStrategy deltaTransactionStrategy = null; + + private StoreStrategy checkStrategy() { + StoreStrategy currentStrategy = strategy; + currentStrategy = mergeStrategies(currentStrategy, transformToImmutable, StoreStrategy.STATE); + currentStrategy = mergeStrategies(currentStrategy, sharingStrategy, StoreStrategy.STATE); + currentStrategy = mergeStrategies(currentStrategy, continousHashProvider, StoreStrategy.STATE); + currentStrategy = mergeStrategies(currentStrategy, deltaTransactionStrategy, StoreStrategy.DELTA); + return currentStrategy; + } + + private StoreStrategy mergeStrategies(StoreStrategy old, StoreStrategy newStrategy) { + if (old != null && newStrategy != null && old != newStrategy) { + throw new IllegalArgumentException("Mixed strategy parametrization in VersionedMap builder!"); + } + + if (old != null) { + return old; + } else { + return newStrategy; + } + } + + private StoreStrategy mergeStrategies(StoreStrategy old, Object parameter, StoreStrategy newStrategy) { + if (parameter != null) { + return mergeStrategies(old, newStrategy); + } else { + return old; + } + } + + @Override + public VersionedMapStoreFactoryBuilder defaultValue(V defaultValue) { + this.defaultSet = true; + this.defaultValue = defaultValue; + return this; + } + + @Override + public VersionedMapStoreFactoryBuilder strategy(StoreStrategy strategy) { + this.strategy = strategy; + checkStrategy(); + return this; + } + + @Override + public VersionedMapStoreFactoryBuilder stateBasedImmutableWhenCommitting(boolean transformToImmutable) { + this.transformToImmutable = transformToImmutable; + checkStrategy(); + return this; + } + + @Override + public VersionedMapStoreFactoryBuilder stateBasedSharingStrategy(SharingStrategy sharingStrategy) { + this.sharingStrategy = sharingStrategy; + checkStrategy(); + return this; + } + + @Override + public VersionedMapStoreFactoryBuilder stateBasedHashProvider(ContinousHashProvider hashProvider) { + this.continousHashProvider = hashProvider; + checkStrategy(); + return this; + } + + @Override + public VersionedMapStoreFactoryBuilder deltaTransactionStrategy(DeltaTransactionStrategy deltaTransactionStrategy) { + this.deltaTransactionStrategy = deltaTransactionStrategy; + checkStrategy(); + return this; + } + + private T getOrDefault(T value, T defaultValue) { + if(value != null) { + return value; + } else { + return defaultValue; + } + } + + @Override + public VersionedMapStoreFactory build() { + if (!defaultSet) { + throw new IllegalArgumentException("Default value is missing!"); + } + var strategyToUse = checkStrategy(); + if (strategyToUse == null) { + return new DeltaBasedVersionedMapStoreFactory<>(defaultValue, + getOrDefault(deltaTransactionStrategy, DeltaTransactionStrategy.LIST)); + } + return switch (strategyToUse) { + case STATE -> { + if(continousHashProvider == null) { + throw new IllegalArgumentException("Continuous hash provider is missing!"); + } + yield new StateBasedVersionedMapStoreFactory<>(defaultValue, + getOrDefault(transformToImmutable,true), + getOrDefault(sharingStrategy, SharingStrategy.SHARED_NODE_CACHE_IN_GROUP), + continousHashProvider); + } + case DELTA -> new DeltaBasedVersionedMapStoreFactory<>(defaultValue, + getOrDefault(deltaTransactionStrategy, DeltaTransactionStrategy.LIST)); + }; + } + + @Override + public String toString() { + return "VersionedMapStoreBuilder{" + + "defaultValue=" + defaultValue + + ", strategy=" + strategy + + ", stateBasedImmutableWhenCommitting=" + transformToImmutable + + ", stateBasedNodeSharingStrategy=" + sharingStrategy + + ", hashProvider=" + continousHashProvider + + ", deltaStorageStrategy=" + deltaTransactionStrategy + + '}'; + } +} diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java index 05cf5a74..993e5531 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java @@ -1,7 +1,8 @@ package tools.refinery.store.map.tests; import org.junit.jupiter.api.Test; -import tools.refinery.store.map.VersionedMapStoreBuilder; +import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; import tools.refinery.store.map.internal.InOrderMapCursor; import tools.refinery.store.map.internal.VersionedMapImpl; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -11,13 +12,14 @@ import static org.junit.jupiter.api.Assertions.*; class InOrderCursorTest { @Test void testCursor() { - var store = VersionedMapStoreBuilder.builder() - .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) - .setStateBasedImmutableWhenCommitting(true) - .setHashProvider(MapTestEnvironment.prepareHashProvider(false)) - .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy.SHARED_NODE_CACHE) - .setDefaultValue("x") - .buildOne(); + var store = VersionedMapStore.builder() + .strategy(VersionedMapStoreFactoryBuilder.StoreStrategy.STATE) + .stateBasedImmutableWhenCommitting(true) + .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) + .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE) + .defaultValue("x") + .build() + .createOne(); VersionedMapImpl map = (VersionedMapImpl) store.createMap(); checkMove(map,0); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java index 2216db76..6889fd07 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/MapUnitTests.java @@ -2,7 +2,6 @@ package tools.refinery.store.map.tests; import org.junit.jupiter.api.Test; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreBuilder; import tools.refinery.store.map.VersionedMapStoreImpl; import tools.refinery.store.model.TupleHashProvider; import tools.refinery.store.tuple.Tuple; @@ -23,7 +22,7 @@ class MapUnitTests { @Test void deltaRestoreTest() { VersionedMapStore store = - VersionedMapStoreBuilder.builder().setDefaultValue("x").buildOne(); + VersionedMapStore.builder().defaultValue("x").build().createOne(); var map = store.createMap(); map.put(1,"val"); var version1 = map.commit(); @@ -36,7 +35,7 @@ class MapUnitTests { @Test void deltaRestoreTest2() { VersionedMapStore store = - VersionedMapStoreBuilder.builder().setDefaultValue("x").buildOne(); + VersionedMapStore.builder().defaultValue("x").build().createOne(); var map = store.createMap(); map.put(1,"x"); var version1 = map.commit(); @@ -48,7 +47,7 @@ class MapUnitTests { @Test void deltaRestoreTest3() { VersionedMapStore store = - VersionedMapStoreBuilder.builder().setDefaultValue("x").buildOne(); + VersionedMapStore.builder().defaultValue("x").build().createOne(); var map = store.createMap(); map.commit(); map.put(1,"1"); @@ -71,7 +70,7 @@ class MapUnitTests { @Test void deltaRestoreTest4() { VersionedMapStore store = - VersionedMapStoreBuilder.builder().setDefaultValue("x").buildOne(); + VersionedMapStore.builder().defaultValue("x").build().createOne(); var map = store.createMap(); map.commit(); map.put(1,"1"); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java index 14a9e2e0..7977f772 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/CommitFuzzTest.java @@ -1,30 +1,29 @@ package tools.refinery.store.map.tests.fuzz; -import static org.junit.jupiter.api.Assertions.fail; - -import java.util.Random; -import java.util.stream.Stream; - import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; - import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreBuilder; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; +import java.util.Random; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.fail; import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; class CommitFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, - boolean nullDefault, int commitFrequency, VersionedMapStoreBuilder builder) { + boolean nullDefault, int commitFrequency, + VersionedMapStoreFactoryBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); + VersionedMapStore store = builder.defaultValue(values[0]).build().createOne(); var sut = store.createMap(); MapTestEnvironment e = new MapTestEnvironment<>(sut); @@ -61,7 +60,7 @@ class CommitFuzzTest { @Timeout(value = 10) @Tag("fuzz") void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, VersionedMapStoreBuilder builder) { + int seed, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, builder); } @@ -76,7 +75,7 @@ class CommitFuzzTest { @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, VersionedMapStoreBuilder builder) { + int seed, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("CommitS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, builder); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java index b462ed40..99e76649 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/ContentEqualsFuzzTest.java @@ -5,7 +5,10 @@ import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import tools.refinery.store.map.*; +import tools.refinery.store.map.Cursor; +import tools.refinery.store.map.VersionedMap; +import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -16,12 +19,13 @@ import java.util.List; import java.util.Random; import java.util.stream.Stream; -import static org.junit.jupiter.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.fail; import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; class ContentEqualsFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, - boolean nullDefault, int commitFrequency, VersionedMapStoreBuilder builder) { + boolean nullDefault, int commitFrequency, + VersionedMapStoreFactoryBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); @@ -30,10 +34,10 @@ class ContentEqualsFuzzTest { iterativeRandomPutsAndCommitsThenCompare(scenario, builder, steps, maxKey, values, r, commitFrequency); } - private void iterativeRandomPutsAndCommitsThenCompare(String scenario, VersionedMapStoreBuilder builder, + private void iterativeRandomPutsAndCommitsThenCompare(String scenario, VersionedMapStoreFactoryBuilder builder, int steps, int maxKey, String[] values, Random r, int commitFrequency) { - VersionedMapStore store1 = builder.setDefaultValue(values[0]).buildOne(); + VersionedMapStore store1 = builder.defaultValue(values[0]).build().createOne(); VersionedMap sut1 = store1.createMap(); // Fill one map @@ -63,7 +67,7 @@ class ContentEqualsFuzzTest { // Randomize the order of the content Collections.shuffle(content, r); - VersionedMapStore store2 = builder.setDefaultValue(values[0]).buildOne(); + VersionedMapStore store2 = builder.defaultValue(values[0]).build().createOne(); VersionedMap sut2 = store2.createMap(); int index2 = 1; for (SimpleEntry entry : content) { @@ -88,7 +92,7 @@ class ContentEqualsFuzzTest { @Timeout(value = 10) @Tag("fuzz") void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, VersionedMapStoreBuilder builder) { + int seed, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, builder); } @@ -103,7 +107,7 @@ class ContentEqualsFuzzTest { @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, int commitFrequency, - int seed, VersionedMapStoreBuilder builder) { + int seed, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("CompareS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, defaultNull, commitFrequency, builder); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java index b087906d..3a8852a9 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java @@ -8,7 +8,7 @@ import org.junit.jupiter.params.provider.MethodSource; import tools.refinery.store.map.DiffCursor; import tools.refinery.store.map.VersionedMap; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreBuilder; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -21,10 +21,10 @@ import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; class DiffCursorFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, boolean commitBeforeDiffCursor, - VersionedMapStoreBuilder builder) { + VersionedMapStoreFactoryBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); + VersionedMapStore store = builder.defaultValue(values[0]).build().createOne(); iterativeRandomPutsAndCommitsThenDiffCursor(scenario, store, steps, maxKey, values, seed, commitFrequency, commitBeforeDiffCursor); } @@ -109,7 +109,7 @@ class DiffCursorFuzzTest { @Tag("fuzz") void parametrizedFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, int seed, boolean commitBeforeDiffCursor, - VersionedMapStoreBuilder builder) { + VersionedMapStoreFactoryBuilder builder) { runFuzzTest("DiffCursorS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, commitBeforeDiffCursor, builder); } @@ -124,7 +124,7 @@ class DiffCursorFuzzTest { @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, boolean commitBeforeDiffCursor, VersionedMapStoreBuilder builder) { + int seed, boolean commitBeforeDiffCursor, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("DiffCursorS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, commitBeforeDiffCursor, builder); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java index ec2224b4..ea58e1b7 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadFuzzTest.java @@ -1,32 +1,31 @@ package tools.refinery.store.map.tests.fuzz; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.fail; -import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; - -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Stream; - import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; - import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreBuilder; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; + class MultiThreadFuzzTest { public static final int noThreads = 10; - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, VersionedMapStoreBuilder builder) { + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, VersionedMapStoreFactoryBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); + VersionedMapStore store = builder.defaultValue(values[0]).build().createOne(); // initialize runnables MultiThreadTestRunnable[] runnables = new MultiThreadTestRunnable[noThreads]; @@ -73,7 +72,7 @@ class MultiThreadFuzzTest { @Timeout(value = 10) @Tag("fuzz") void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, - int commitFrequency, int seed, VersionedMapStoreBuilder builder) { + int commitFrequency, int seed, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("MultiThreadS" + steps + "K" + noKeys + "V" + noValues + defaultNull + "CF" + commitFrequency + "s" + seed, seed, steps, noKeys, noValues, defaultNull, commitFrequency, builder); } @@ -88,7 +87,7 @@ class MultiThreadFuzzTest { @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, - int commitFrequency, int seed, VersionedMapStoreBuilder builder) { + int commitFrequency, int seed, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, builder); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java index bdf72ce4..61b17362 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableFuzzTest.java @@ -18,10 +18,10 @@ import tools.refinery.store.map.tests.utils.MapTestEnvironment; class MutableFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, - boolean nullDefault, VersionedMapStoreBuilder builder) { + boolean nullDefault, VersionedMapStoreFactoryBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); + VersionedMapStore store = builder.defaultValue(values[0]).build().createOne(); VersionedMap sut = store.createMap(); MapTestEnvironment e = new MapTestEnvironment<>(sut); @@ -56,7 +56,7 @@ class MutableFuzzTest { @Timeout(value = 10) @Tag("fuzz") void parametrizedFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, int seed, - VersionedMapStoreBuilder builder) { + VersionedMapStoreFactoryBuilder builder) { runFuzzTest( "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, defaultNull, builder); @@ -72,7 +72,7 @@ class MutableFuzzTest { @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int seed, - VersionedMapStoreBuilder builder) { + VersionedMapStoreFactoryBuilder builder) { runFuzzTest( "MutableS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, builder); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java index 568aaac9..1661cccb 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java @@ -1,31 +1,31 @@ package tools.refinery.store.map.tests.fuzz; -import static org.junit.jupiter.api.Assertions.fail; -import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; - -import java.util.HashMap; -import java.util.Map; -import java.util.Random; -import java.util.stream.Stream; - import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; - -import tools.refinery.store.map.*; -import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.VersionedMap; +import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; +import java.util.HashMap; +import java.util.Map; +import java.util.Random; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.fail; +import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; + class RestoreFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, - VersionedMapStoreBuilder builder) { + VersionedMapStoreFactoryBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); + VersionedMapStore store = builder.defaultValue(values[0]).build().createOne(); iterativeRandomPutsAndCommitsThenRestore(scenario, store, steps, maxKey, values, seed, commitFrequency); } @@ -84,7 +84,7 @@ class RestoreFuzzTest { @Timeout(value = 10) @Tag("smoke") void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, VersionedMapStoreBuilder builder) { + int seed, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, builder); } @@ -99,7 +99,7 @@ class RestoreFuzzTest { @Tag("smoke") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, int commitFrequency, - int seed, VersionedMapStoreBuilder builder) { + int seed, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("RestoreS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, builder); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java index e7d49227..0e1f9f9f 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java @@ -6,7 +6,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreBuilder; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -15,10 +15,10 @@ import java.util.stream.Stream; import static tools.refinery.store.map.tests.fuzz.utils.FuzzTestCollections.*; class SingleThreadFuzzTest { - private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, VersionedMapStoreBuilder builder) { + private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, VersionedMapStoreFactoryBuilder builder) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - VersionedMapStore store = builder.setDefaultValue(values[0]).buildOne(); + VersionedMapStore store = builder.defaultValue(values[0]).build().createOne(); // initialize runnables MultiThreadTestRunnable runnable = new MultiThreadTestRunnable(scenario, store, steps, maxKey, values, seed, commitFrequency); @@ -35,7 +35,7 @@ class SingleThreadFuzzTest { @Timeout(value = 10) @Tag("fuzz") void parametrizedFastFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean defaultNull, - int commitFrequency, int seed, VersionedMapStoreBuilder builder) { + int commitFrequency, int seed, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("SingleThreadS" + steps + "K" + noKeys + "V" + noValues + defaultNull + "CF" + commitFrequency + "s" + seed, seed, steps, noKeys, noValues, defaultNull, commitFrequency, builder); } @@ -50,7 +50,7 @@ class SingleThreadFuzzTest { @Tag("fuzz") @Tag("slow") void parametrizedSlowFuzz(int ignoredTests, int steps, int noKeys, int noValues, boolean nullDefault, - int commitFrequency, int seed, VersionedMapStoreBuilder builder) { + int commitFrequency, int seed, VersionedMapStoreFactoryBuilder builder) { runFuzzTest("SingleThreadS" + steps + "K" + noKeys + "V" + noValues + "s" + seed, seed, steps, noKeys, noValues, nullDefault, commitFrequency, builder); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java index b344d9b9..94c9cba7 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java @@ -1,6 +1,7 @@ package tools.refinery.store.map.tests.fuzz.utils; -import tools.refinery.store.map.VersionedMapStoreBuilder; +import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; import tools.refinery.store.map.tests.utils.MapTestEnvironment; public final class FuzzTestCollections { @@ -12,34 +13,27 @@ public final class FuzzTestCollections { public static final Object[] randomSeedOptions = {1}; public static final Object[] storeConfigs = { // State based - VersionedMapStoreBuilder.builder() - .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) - .setStateBasedImmutableWhenCommitting(true) - .setHashProvider(MapTestEnvironment.prepareHashProvider(false)) - .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy - .SHARED_NODE_CACHE), - VersionedMapStoreBuilder.builder() - .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) - .setStateBasedImmutableWhenCommitting(true) - .setHashProvider(MapTestEnvironment.prepareHashProvider(true)) - .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy - .SHARED_NODE_CACHE), - VersionedMapStoreBuilder.builder() - .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) - .setStateBasedImmutableWhenCommitting(false) - .setHashProvider(MapTestEnvironment.prepareHashProvider(false)) - .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy.SHARED_NODE_CACHE), - VersionedMapStoreBuilder.builder() - .setStrategy(VersionedMapStoreBuilder.StoreStrategy.STATE) - .setStateBasedImmutableWhenCommitting(false) - .setHashProvider(MapTestEnvironment.prepareHashProvider(false)) - .setStateBasedNodeSharingStrategy(VersionedMapStoreBuilder.StateStorageStrategy.NO_NODE_CACHE), + VersionedMapStore.builder() + .stateBasedImmutableWhenCommitting(true) + .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) + .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + VersionedMapStore.builder() + .stateBasedImmutableWhenCommitting(true) + .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(true)) + .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + VersionedMapStore.builder() + .stateBasedImmutableWhenCommitting(false) + .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) + .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + VersionedMapStore.builder() + .stateBasedImmutableWhenCommitting(false) + .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) + .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.NO_NODE_CACHE), + // Delta based - VersionedMapStoreBuilder.builder() - .setStrategy(VersionedMapStoreBuilder.StoreStrategy.DELTA) - .setDeltaStorageStrategy(VersionedMapStoreBuilder.DeltaStorageStrategy.SET), - VersionedMapStoreBuilder.builder() - .setStrategy(VersionedMapStoreBuilder.StoreStrategy.DELTA) - .setDeltaStorageStrategy(VersionedMapStoreBuilder.DeltaStorageStrategy.LIST) + VersionedMapStore.builder() + .deltaTransactionStrategy(VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy.SET), + VersionedMapStore.builder() + .deltaTransactionStrategy(VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy.LIST) }; } -- cgit v1.2.3-54-g00ecf From 04860420ded195f9434477dab33309da61edc7e9 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Fri, 21 Jul 2023 21:14:18 +0200 Subject: Improved tuple hash calculation - by special handling of unary and binary relations - introducing murmur32Scramble --- .../refinery/store/model/TupleHashProvider.java | 125 +++++++++++++++------ 1 file changed, 91 insertions(+), 34 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java b/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java index 4bcf9ff4..1183b8f2 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java @@ -2,39 +2,43 @@ package tools.refinery.store.model; import tools.refinery.store.map.ContinousHashProvider; import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; +import tools.refinery.store.tuple.Tuple2; public class TupleHashProvider implements ContinousHashProvider { - protected static final int[] primes = new int[] { 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, - 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, - 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, - 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, - 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, - 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, - 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, - 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021, - 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, 1103, 1109, 1117, 1123, 1129, 1151, - 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, 1237, 1249, 1259, 1277, 1279, 1283, - 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, 1381, 1399, 1409, 1423, 1427, 1429, - 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, 1499, 1511, 1523, 1531, 1543, 1549, - 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, 1621, 1627, 1637, 1657, 1663, 1667, - 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, 1777, 1783, 1787, 1789, 1801, 1811, - 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, 1913, 1931, 1933, 1949, 1951, 1973, - 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, 2063, 2069, 2081, 2083, 2087, 2089, - 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, 2207, 2213, 2221, 2237, 2239, 2243, - 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, 2341, 2347, 2351, 2357, 2371, 2377, - 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, 2467, 2473, 2477, 2503, 2521, 2531, - 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, 2647, 2657, 2659, 2663, 2671, 2677, - 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, 2749, 2753, 2767, 2777, 2789, 2791, - 2797, 2801, 2803, 2819, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, 3181, 3187, 3191, 3203, 3209, 3217, - 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, 3323, 3329, 3331, 3343, 3347, 3359, - 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, 3467, 3469, 3491, 3499, 3511, 3517, - 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, 3607, 3613, 3617, 3623, 3631, 3637, - 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, 3739, 3761, 3767, 3769, 3779, 3793, - 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, 3907, 3911 }; - - public static final long LARGEST_PRIME_30_BITS = 1073741789; - - public static final int MAX_MODEL_SIZE = (int) LARGEST_PRIME_30_BITS; + protected static final int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, + 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, + 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, + 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, + 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, + 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, + 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, + 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, + 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, 1097, + 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229, 1231, + 1237, 1249, 1259, 1277, 1279, 1283, 1289, 1291, 1297, 1301, 1303, 1307, 1319, 1321, 1327, 1361, 1367, 1373, + 1381, 1399, 1409, 1423, 1427, 1429, 1433, 1439, 1447, 1451, 1453, 1459, 1471, 1481, 1483, 1487, 1489, 1493, + 1499, 1511, 1523, 1531, 1543, 1549, 1553, 1559, 1567, 1571, 1579, 1583, 1597, 1601, 1607, 1609, 1613, 1619, + 1621, 1627, 1637, 1657, 1663, 1667, 1669, 1693, 1697, 1699, 1709, 1721, 1723, 1733, 1741, 1747, 1753, 1759, + 1777, 1783, 1787, 1789, 1801, 1811, 1823, 1831, 1847, 1861, 1867, 1871, 1873, 1877, 1879, 1889, 1901, 1907, + 1913, 1931, 1933, 1949, 1951, 1973, 1979, 1987, 1993, 1997, 1999, 2003, 2011, 2017, 2027, 2029, 2039, 2053, + 2063, 2069, 2081, 2083, 2087, 2089, 2099, 2111, 2113, 2129, 2131, 2137, 2141, 2143, 2153, 2161, 2179, 2203, + 2207, 2213, 2221, 2237, 2239, 2243, 2251, 2267, 2269, 2273, 2281, 2287, 2293, 2297, 2309, 2311, 2333, 2339, + 2341, 2347, 2351, 2357, 2371, 2377, 2381, 2383, 2389, 2393, 2399, 2411, 2417, 2423, 2437, 2441, 2447, 2459, + 2467, 2473, 2477, 2503, 2521, 2531, 2539, 2543, 2549, 2551, 2557, 2579, 2591, 2593, 2609, 2617, 2621, 2633, + 2647, 2657, 2659, 2663, 2671, 2677, 2683, 2687, 2689, 2693, 2699, 2707, 2711, 2713, 2719, 2729, 2731, 2741, + 2749, 2753, 2767, 2777, 2789, 2791, 2797, 2801, 2803, 2819, 3089, 3109, 3119, 3121, 3137, 3163, 3167, 3169, + 3181, 3187, 3191, 3203, 3209, 3217, 3221, 3229, 3251, 3253, 3257, 3259, 3271, 3299, 3301, 3307, 3313, 3319, + 3323, 3329, 3331, 3343, 3347, 3359, 3361, 3371, 3373, 3389, 3391, 3407, 3413, 3433, 3449, 3457, 3461, 3463, + 3467, 3469, 3491, 3499, 3511, 3517, 3527, 3529, 3533, 3539, 3541, 3547, 3557, 3559, 3571, 3581, 3583, 3593, + 3607, 3613, 3617, 3623, 3631, 3637, 3643, 3659, 3671, 3673, 3677, 3691, 3697, 3701, 3709, 3719, 3727, 3733, + 3739, 3761, 3767, 3769, 3779, 3793, 3797, 3803, 3821, 3823, 3833, 3847, 3851, 3853, 3863, 3877, 3881, 3889, + 3907, 3911 }; + + protected static final long LARGEST_PRIME_30_BITS_LONG = 1073741789; + protected static final int LARGEST_PRIME_30_BITS_INTEGER = 1073741789; + protected static final int LARGEST_BINARY_INDEX_1 = LARGEST_PRIME_30_BITS_INTEGER / 4; + public static final int MAX_MODEL_SIZE = LARGEST_PRIME_30_BITS_INTEGER; public static final TupleHashProvider INSTANCE = new TupleHashProvider(); @@ -47,15 +51,68 @@ public class TupleHashProvider implements ContinousHashProvider { @Override public int getHash(Tuple key, int index) { - if (index >= primes.length) { - throw new IllegalArgumentException("Not enough prime numbers to support index"); + if(key instanceof Tuple1 t1) { + return t1.value0(); + } else if(key instanceof Tuple2 t2){ + if(index == 0) { + return murmur3T2(t2.value0(), t2.value1()); + } else if(index == 1) { + return lagrangeT2I0Quick(t2); + } else if(index == 2) { + return lagrangeT2I1Quick(t2); + } else { + return lagrangeTXIX(key, index-1); + } + } else { + return lagrangeTXIX(key, index); } + + } + + private static int lagrangeT2I0Quick(Tuple2 t2) { + int result = 2 * t2.value0() + t2.value1(); + if (result > LARGEST_PRIME_30_BITS_INTEGER) { + return result % LARGEST_PRIME_30_BITS_INTEGER; + } else + return result; + } + private static int lagrangeT2I1Quick(Tuple2 t2) { + int value0 = t2.value0(); + int value1 = t2.value1(); + if(value0 < LARGEST_BINARY_INDEX_1 && value1 < LARGEST_BINARY_INDEX_1) { + return 3* value0 + value1; + } else { + return lagrangeTXIX(t2, 1); + } + } + + private static int lagrangeTXIX(Tuple key, int index) { long accumulator = 0; final int prime = primes[index]; for (int i = 0; i < key.getSize(); i++) { - accumulator = (prime * accumulator + key.get(i)) % MAX_MODEL_SIZE; + accumulator = (prime * accumulator + key.get(i)) % LARGEST_PRIME_30_BITS_LONG; } return (int) accumulator; } + + private static int murmur3T2(int v0, int v1) + { + int h = 0; + + h = murmur32Scramble(v0, h); + h = murmur32Scramble(v1, h); + + return h; + } + + private static int murmur32Scramble(int k, int h) { + k *= 0xcc9e2d51; + k = (k << 15) | (k >>> 17); + k *= 0x1b873593; + h ^= k; + h = (h << 13) | (h >>> 19); + h = h * 5 + 0xe6546b64; + return h; + } } -- cgit v1.2.3-54-g00ecf From 38c66c13d3003df1f901258b4ffc69b1fac980e8 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Fri, 21 Jul 2023 21:16:43 +0200 Subject: decreasing steps in fast fuzz tests --- .../java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java | 2 +- .../java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java index 3a8852a9..e02448cf 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java @@ -115,7 +115,7 @@ class DiffCursorFuzzTest { } static Stream parametrizedFuzz() { - return FuzzTestUtils.permutationWithSize(new Object[]{500}, keyCounts, valueCounts, nullDefaultOptions, + return FuzzTestUtils.permutationWithSize(new Object[]{100}, keyCounts, valueCounts, nullDefaultOptions, commitFrequencyOptions, randomSeedOptions, new Object[]{false,true}, storeConfigs); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java index 92208e48..89c01690 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestUtils.java @@ -8,7 +8,7 @@ import java.util.stream.Stream; import org.junit.jupiter.params.provider.Arguments; public final class FuzzTestUtils { - public static final int FAST_STEP_COUNT = 500; + public static final int FAST_STEP_COUNT = 250; public static final int SLOW_STEP_COUNT = 32 * 32 * 32 * 32; private FuzzTestUtils() { -- cgit v1.2.3-54-g00ecf From 3455aeab401aeab4f01d8ef67a73b965b0a0dde0 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Mon, 24 Jul 2023 15:08:46 +0200 Subject: Added missing copyright headers. --- .../java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java | 5 +++++ .../main/java/tools/refinery/store/map/VersionedMapStoreFactory.java | 5 +++++ .../tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java | 5 +++++ .../store/map/internal/DeltaBasedVersionedMapStoreFactory.java | 5 +++++ .../main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java | 5 +++++ .../java/tools/refinery/store/map/internal/InOrderMapCursor.java | 5 +++++ .../java/tools/refinery/store/map/internal/IteratorAsCursor.java | 5 +++++ .../src/main/java/tools/refinery/store/map/internal/MapDelta.java | 5 +++++ .../main/java/tools/refinery/store/map/internal/MapTransaction.java | 5 +++++ .../store/map/internal/StateBasedVersionedMapStoreFactory.java | 5 +++++ .../refinery/store/map/internal/UncommittedDeltaArrayStore.java | 5 +++++ .../tools/refinery/store/map/internal/UncommittedDeltaMapStore.java | 5 +++++ .../tools/refinery/store/map/internal/UncommittedDeltaStore.java | 5 +++++ .../tools/refinery/store/map/internal/VersionedMapDeltaImpl.java | 5 +++++ .../store/map/internal/VersionedMapStoreFactoryBuilderImpl.java | 5 +++++ .../test/java/tools/refinery/store/map/tests/InOrderCursorTest.java | 5 +++++ .../tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java | 5 +++++ .../refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java | 5 +++++ 18 files changed, 90 insertions(+) (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java index 31cdbf95..0c61bd09 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map; import java.util.*; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactory.java index 5f882a3a..baf6ab50 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactory.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactory.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map; import java.util.List; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java index 9cf17b49..6329a2f6 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map; public interface VersionedMapStoreFactoryBuilder { diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java index 29ec0da1..fe490f46 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; import tools.refinery.store.map.VersionedMapStore; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java index 8ddca8ec..cc9003e3 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; import tools.refinery.store.map.AnyVersionedMap; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java index c559d9ad..cb3f366f 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; import tools.refinery.store.map.AnyVersionedMap; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java index c1a0aec4..d1ab8bb1 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; import java.util.*; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java index 86e9fe62..2674236c 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; public record MapDelta(K key, V oldValue, V newValue) { diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java index 5996048e..d63522cd 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; import java.util.Arrays; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java index 80dc347f..1c3ab27b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; import tools.refinery.store.map.*; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java index 3b3f94ae..ba59cfef 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; import java.util.ArrayList; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java index 31423b1c..61a34351 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; import java.util.*; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java index 7b017c8e..438b5561 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; public interface UncommittedDeltaStore { diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java index d09e54ba..ae47feda 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; import java.util.*; diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java index 3719eef5..cf117d95 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.internal; import tools.refinery.store.map.ContinousHashProvider; diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java index 993e5531..4ada4ea4 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.tests; import org.junit.jupiter.api.Test; diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java index 0e1f9f9f..1337cf3a 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SingleThreadFuzzTest.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.tests.fuzz; import org.junit.jupiter.api.Tag; diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java index 94c9cba7..4c3ecb09 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.map.tests.fuzz.utils; import tools.refinery.store.map.VersionedMapStore; -- cgit v1.2.3-54-g00ecf From f25e1f589ba7c9984af4c77776f43d6746bf98b1 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Mon, 24 Jul 2023 15:09:20 +0200 Subject: Enabled QueryTransactionTest --- .../java/tools/refinery/store/query/viatra/QueryTransactionTest.java | 3 --- 1 file changed, 3 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java index 66f043c6..32f51cf0 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java @@ -6,7 +6,6 @@ package tools.refinery.store.query.viatra; import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; @@ -283,7 +282,6 @@ class QueryTransactionTest { assertResults(Map.of(Tuple.of(0), false), queryResultSet); } - @Disabled("TODO Fix DiffCursor") @Test void commitAfterFlushTest() { var store = ModelStore.builder() @@ -332,7 +330,6 @@ class QueryTransactionTest { ), predicateResultSet); } - @Disabled("TODO Fix DiffCursor") @Test void commitWithoutFlushTest() { var store = ModelStore.builder() -- cgit v1.2.3-54-g00ecf From 26592fc70a026b850616fc4bc9be5a46ab1179a9 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Mon, 24 Jul 2023 16:51:47 +0200 Subject: Refactoring packages related to VersionedMapDeltaImpl + VersionedMapStoreStateImpl, update builder. - details of the maps goes to internal packages - ModelStoreBuilderImpl uses VersionedMapStoreFactoryBuilder --- .../map/benchmarks/ImmutablePutExecutionPlan.java | 17 +- .../refinery/store/map/ContinousHashProvider.java | 74 --- .../refinery/store/map/ContinuousHashProvider.java | 74 +++ .../tools/refinery/store/map/IteratorAsCursor.java | 64 +++ .../store/map/VersionedMapStoreConfiguration.java | 53 --- .../store/map/VersionedMapStoreDeltaImpl.java | 99 ---- .../store/map/VersionedMapStoreFactoryBuilder.java | 2 +- .../refinery/store/map/VersionedMapStoreImpl.java | 129 ------ .../DeltaBasedVersionedMapStoreFactory.java | 39 -- .../store/map/internal/DeltaDiffCursor.java | 147 ------ .../refinery/store/map/internal/HashClash.java | 23 - .../refinery/store/map/internal/ImmutableNode.java | 413 ----------------- .../store/map/internal/InOrderMapCursor.java | 146 ------ .../store/map/internal/IteratorAsCursor.java | 66 --- .../refinery/store/map/internal/MapCursor.java | 95 ---- .../refinery/store/map/internal/MapDelta.java | 20 - .../refinery/store/map/internal/MapDiffCursor.java | 264 ----------- .../store/map/internal/MapTransaction.java | 39 -- .../refinery/store/map/internal/MutableNode.java | 499 --------------------- .../tools/refinery/store/map/internal/Node.java | 131 ------ .../refinery/store/map/internal/OldValueBox.java | 24 - .../StateBasedVersionedMapStoreFactory.java | 38 -- .../map/internal/UncommittedDeltaArrayStore.java | 36 -- .../map/internal/UncommittedDeltaMapStore.java | 53 --- .../store/map/internal/UncommittedDeltaStore.java | 29 -- .../store/map/internal/VersionedMapDeltaImpl.java | 219 --------- .../store/map/internal/VersionedMapImpl.java | 171 ------- .../VersionedMapStoreFactoryBuilderImpl.java | 18 +- .../delta/DeltaBasedVersionedMapStoreFactory.java | 38 ++ .../store/map/internal/delta/DeltaDiffCursor.java | 147 ++++++ .../store/map/internal/delta/MapDelta.java | 20 + .../store/map/internal/delta/MapTransaction.java | 39 ++ .../internal/delta/UncommittedDeltaArrayStore.java | 36 ++ .../internal/delta/UncommittedDeltaMapStore.java | 53 +++ .../map/internal/delta/UncommittedDeltaStore.java | 29 ++ .../map/internal/delta/VersionedMapDeltaImpl.java | 220 +++++++++ .../internal/delta/VersionedMapStoreDeltaImpl.java | 101 +++++ .../store/map/internal/state/ImmutableNode.java | 413 +++++++++++++++++ .../store/map/internal/state/InOrderMapCursor.java | 146 ++++++ .../store/map/internal/state/MapCursor.java | 95 ++++ .../store/map/internal/state/MapDiffCursor.java | 264 +++++++++++ .../store/map/internal/state/MutableNode.java | 499 +++++++++++++++++++++ .../refinery/store/map/internal/state/Node.java | 131 ++++++ .../store/map/internal/state/OldValueBox.java | 24 + .../state/StateBasedVersionedMapStoreFactory.java | 38 ++ .../map/internal/state/VersionedMapStateImpl.java | 171 +++++++ .../state/VersionedMapStoreStateConfiguration.java | 56 +++ .../internal/state/VersionedMapStoreStateImpl.java | 129 ++++++ .../refinery/store/model/TupleHashProvider.java | 4 +- .../store/model/TupleHashProviderBitMagic.java | 34 -- .../model/internal/ModelStoreBuilderImpl.java | 12 +- .../store/map/tests/InOrderCursorTest.java | 8 +- .../refinery/store/map/tests/MapUnitTests.java | 4 +- .../fuzz/MutableImmutableCompareFuzzTest.java | 18 +- .../store/map/tests/fuzz/SharedStoreFuzzTest.java | 18 +- .../store/map/tests/utils/MapTestEnvironment.java | 6 +- .../store/model/hashtests/HashEfficiencyTest.java | 13 +- 57 files changed, 2849 insertions(+), 2899 deletions(-) delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/ContinousHashProvider.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/ContinuousHashProvider.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/IteratorAsCursor.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreConfiguration.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/OldValueBox.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/DeltaBasedVersionedMapStoreFactory.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/DeltaDiffCursor.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapDelta.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaArrayStore.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaMapStore.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaStore.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/InOrderMapCursor.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MapCursor.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MapDiffCursor.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MutableNode.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/Node.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/OldValueBox.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProviderBitMagic.java (limited to 'subprojects') diff --git a/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java b/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java index 7e89cd06..4708e6d3 100644 --- a/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java +++ b/subprojects/store/src/jmh/java/tools/refinery/store/map/benchmarks/ImmutablePutExecutionPlan.java @@ -5,12 +5,13 @@ */ package tools.refinery.store.map.benchmarks; +import java.util.Objects; import java.util.Random; -import tools.refinery.store.map.ContinousHashProvider; +import tools.refinery.store.map.ContinuousHashProvider; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreImpl; -import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.internal.state.VersionedMapStoreStateImpl; +import tools.refinery.store.map.internal.state.VersionedMapStateImpl; import tools.refinery.store.map.tests.utils.MapTestEnvironment; import org.openjdk.jmh.annotations.Level; @@ -35,7 +36,7 @@ public class ImmutablePutExecutionPlan { private String[] values; - private ContinousHashProvider hashProvider = MapTestEnvironment.prepareHashProvider(false); + private ContinuousHashProvider hashProvider = MapTestEnvironment.prepareHashProvider(false); @Setup(Level.Trial) public void setUpTrial() { @@ -43,9 +44,9 @@ public class ImmutablePutExecutionPlan { values = MapTestEnvironment.prepareValues(nValues, true); } - public VersionedMapImpl createSut() { - VersionedMapStore store = new VersionedMapStoreImpl(hashProvider, values[0]); - return (VersionedMapImpl) store.createMap(); + public VersionedMapStateImpl createSut() { + VersionedMapStore store = new VersionedMapStoreStateImpl<>(hashProvider, values[0]); + return (VersionedMapStateImpl) store.createMap(); } public Integer nextKey() { @@ -53,7 +54,7 @@ public class ImmutablePutExecutionPlan { } public boolean isDefault(String value) { - return value == values[0]; + return Objects.equals(value,values[0]); } public String nextValue() { diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/ContinousHashProvider.java b/subprojects/store/src/main/java/tools/refinery/store/map/ContinousHashProvider.java deleted file mode 100644 index 8e451230..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/ContinousHashProvider.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map; - -import tools.refinery.store.map.internal.Node; - -/** - * A class representing an equivalence relation for a type {@code K} with a - * continuous hash function. - * - * @author Oszkar Semerath - * - * @param Target java type. - */ -public interface ContinousHashProvider { - public static final int EFFECTIVE_BITS = Node.EFFECTIVE_BITS; - public static final int EFFECTIVE_BIT_MASK = (1 << (EFFECTIVE_BITS)) - 1; - - /** - * Maximal practical depth for differentiating keys. If two keys have the same - * hash code until that depth, the algorithm can stop. - */ - public static final int MAX_PRACTICAL_DEPTH = 500; - - /** - * Provides a hash code for a object {@code key} with a given {@code index}. It - * has the following contracts: - *
    - *
  • If {@link #equals}{@code (key1,key2)}, then - * {@code getHash(key1, index) == getHash(key2, index)} for all values of - * {@code index}.
  • - *
  • If {@code getHash(key1,index) == getHash(key2, index)} for all values of - * {@code index}, then {@link #equals}{@code (key1, key2)}
  • - *
  • In current implementation, we use only the least significant - * {@link #EFFECTIVE_BITS} - *
- * Check {@link #equals} for further details. - * - * @param key The target data object. - * @param index The depth of the the hash code. Needs to be non-negative. - * @return A hash code. - */ - public int getHash(K key, int index); - - public default int getEffectiveHash(K key, int index) { - return getHash(key, index) & EFFECTIVE_BIT_MASK; - } - - public default int compare(K key1, K key2) { - if (key1.equals(key2)) { - return 0; - } else { - for (int i = 0; i < ContinousHashProvider.MAX_PRACTICAL_DEPTH; i++) { - int hash1 = getEffectiveHash(key1, i); - int hash2 = getEffectiveHash(key2, i); - for(int j = 0; j>>j*Node.BRANCHING_FACTOR_BITS) & factorMask; - int hashFragment2 = (hash2>>>j*Node.BRANCHING_FACTOR_BITS) & factorMask; - var result = Integer.compare(hashFragment1, hashFragment2); - if (result != 0) { - return result; - } - } - } - throw new IllegalArgumentException("Two different keys (" + key1 + " and " + key2 - + ") have the same hashcode over the practical depth limitation (" - + ContinousHashProvider.MAX_PRACTICAL_DEPTH + ")!"); - } - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/ContinuousHashProvider.java b/subprojects/store/src/main/java/tools/refinery/store/map/ContinuousHashProvider.java new file mode 100644 index 00000000..abc044d0 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/ContinuousHashProvider.java @@ -0,0 +1,74 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map; + +import tools.refinery.store.map.internal.state.Node; + +/** + * A class representing an equivalence relation for a type {@code K} with a + * continuous hash function. + * + * @author Oszkar Semerath + * + * @param Target java type. + */ +public interface ContinuousHashProvider { + public static final int EFFECTIVE_BITS = Node.EFFECTIVE_BITS; + public static final int EFFECTIVE_BIT_MASK = (1 << (EFFECTIVE_BITS)) - 1; + + /** + * Maximal practical depth for differentiating keys. If two keys have the same + * hash code until that depth, the algorithm can stop. + */ + public static final int MAX_PRACTICAL_DEPTH = 500; + + /** + * Provides a hash code for a object {@code key} with a given {@code index}. It + * has the following contracts: + *
    + *
  • If {@link #equals}{@code (key1,key2)}, then + * {@code getHash(key1, index) == getHash(key2, index)} for all values of + * {@code index}.
  • + *
  • If {@code getHash(key1,index) == getHash(key2, index)} for all values of + * {@code index}, then {@link #equals}{@code (key1, key2)}
  • + *
  • In current implementation, we use only the least significant + * {@link #EFFECTIVE_BITS} + *
+ * Check {@link #equals} for further details. + * + * @param key The target data object. + * @param index The depth of the hash code. Needs to be non-negative. + * @return A hash code. + */ + public int getHash(K key, int index); + + public default int getEffectiveHash(K key, int index) { + return getHash(key, index) & EFFECTIVE_BIT_MASK; + } + + public default int compare(K key1, K key2) { + if (key1.equals(key2)) { + return 0; + } else { + for (int i = 0; i < ContinuousHashProvider.MAX_PRACTICAL_DEPTH; i++) { + int hash1 = getEffectiveHash(key1, i); + int hash2 = getEffectiveHash(key2, i); + for(int j = 0; j>>j*Node.BRANCHING_FACTOR_BITS) & factorMask; + int hashFragment2 = (hash2>>>j*Node.BRANCHING_FACTOR_BITS) & factorMask; + var result = Integer.compare(hashFragment1, hashFragment2); + if (result != 0) { + return result; + } + } + } + throw new IllegalArgumentException("Two different keys (" + key1 + " and " + key2 + + ") have the same hashcode over the practical depth limitation (" + + ContinuousHashProvider.MAX_PRACTICAL_DEPTH + ")!"); + } + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/IteratorAsCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/IteratorAsCursor.java new file mode 100644 index 00000000..29b03edf --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/IteratorAsCursor.java @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class IteratorAsCursor implements Cursor { + final Iterator> iterator; + final VersionedMap source; + + private boolean terminated; + private K key; + private V value; + + public IteratorAsCursor(VersionedMap source, Map current) { + this.iterator = current.entrySet().iterator(); + this.source = source; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public boolean isTerminated() { + return terminated; + } + + @Override + public boolean move() { + terminated = !iterator.hasNext(); + if (terminated) { + this.key = null; + this.value = null; + } else { + Entry next = iterator.next(); + this.key = next.getKey(); + this.value = next.getValue(); + } + return !terminated; + } + + @Override + public boolean isDirty() { + return false; + } + + @Override + public Set getDependingMaps() { + return Set.of(this.source); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreConfiguration.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreConfiguration.java deleted file mode 100644 index b00cd961..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreConfiguration.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map; - -public class VersionedMapStoreConfiguration { - - public VersionedMapStoreConfiguration() { - - } - public VersionedMapStoreConfiguration(boolean immutableWhenCommitting, boolean sharedNodeCacheInStore, - boolean sharedNodeCacheInStoreGroups) { - super(); - this.immutableWhenCommitting = immutableWhenCommitting; - this.sharedNodeCacheInStore = sharedNodeCacheInStore; - this.sharedNodeCacheInStoreGroups = sharedNodeCacheInStoreGroups; - } - - /** - * If true root is replaced with immutable node when committed. Frees up memory - * by releasing immutable nodes, but it may decrease performance by recreating - * immutable nodes upon changes (some evidence). - */ - private boolean immutableWhenCommitting = true; - public boolean isImmutableWhenCommitting() { - return immutableWhenCommitting; - } - - /** - * If true, all sub-nodes are cached within a {@link VersionedMapStore}. It - * decreases the memory requirements. It may increase performance by discovering - * existing immutable copy of a node (some evidence). Additional overhead may - * decrease performance (no example found). The option permits the efficient - * implementation of version deletion. - */ - private boolean sharedNodeCacheInStore = true; - public boolean isSharedNodeCacheInStore() { - return sharedNodeCacheInStore; - } - - /** - * If true, all sub-nodes are cached within a group of - * {@link VersionedMapStoreImpl#createSharedVersionedMapStores(int, ContinousHashProvider, Object, VersionedMapStoreConfiguration)}. - * If {@link VersionedMapStoreConfiguration#sharedNodeCacheInStore} is - * false, then it has currently no impact. - */ - private boolean sharedNodeCacheInStoreGroups = true; - public boolean isSharedNodeCacheInStoreGroups() { - return sharedNodeCacheInStoreGroups; - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java deleted file mode 100644 index 0c61bd09..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreDeltaImpl.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map; - -import java.util.*; - -import tools.refinery.store.map.internal.*; - -public class VersionedMapStoreDeltaImpl implements VersionedMapStore { - // Configuration - protected final boolean summarizeChanges; - - // Static data - protected final V defaultValue; - - // Dynamic data - protected final Map> states = new HashMap<>(); - protected long nextID = 0; - - public VersionedMapStoreDeltaImpl(boolean summarizeChanges, V defaultValue) { - this.summarizeChanges = summarizeChanges; - this.defaultValue = defaultValue; - } - - @Override - public VersionedMap createMap() { - return new VersionedMapDeltaImpl<>(this, this.summarizeChanges, this.defaultValue); - } - - @Override - public VersionedMap createMap(long state) { - VersionedMapDeltaImpl result = new VersionedMapDeltaImpl<>(this, this.summarizeChanges, this.defaultValue); - result.restore(state); - return result; - } - - public synchronized MapTransaction appendTransaction(MapDelta[] deltas, MapTransaction previous, long[] versionContainer) { - long version = nextID++; - versionContainer[0] = version; - if (deltas == null) { - states.put(version, previous); - return previous; - } else { - MapTransaction transaction = new MapTransaction<>(deltas, version, previous); - states.put(version, transaction); - return transaction; - } - } - - private synchronized MapTransaction getState(long state) { - return states.get(state); - } - - public MapTransaction getPath(long to, List[]> forwardTransactions) { - final MapTransaction target = getState(to); - MapTransaction toTransaction = target; - while (toTransaction != null) { - forwardTransactions.add(toTransaction.deltas()); - toTransaction = toTransaction.parent(); - } - return target; - } - - public MapTransaction getPath(long from, long to, - List[]> backwardTransactions, - List[]> forwardTransactions) { - MapTransaction fromTransaction = getState(from); - final MapTransaction target = getState(to); - MapTransaction toTransaction = target; - - while (fromTransaction != toTransaction) { - if (fromTransaction == null || (toTransaction != null && fromTransaction.version() < toTransaction.version())) { - forwardTransactions.add(toTransaction.deltas()); - toTransaction = toTransaction.parent(); - } else { - backwardTransactions.add(fromTransaction.deltas()); - fromTransaction = fromTransaction.parent(); - } - } - return target; - } - - - @Override - public synchronized Set getStates() { - return new HashSet<>(states.keySet()); - } - - @Override - public DiffCursor getDiffCursor(long fromState, long toState) { - List[]> backwardTransactions = new ArrayList<>(); - List[]> forwardTransactions = new ArrayList<>(); - getPath(fromState, toState, backwardTransactions, forwardTransactions); - return new DeltaDiffCursor<>(backwardTransactions, forwardTransactions); - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java index 6329a2f6..6b4fc2a0 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java @@ -22,7 +22,7 @@ public interface VersionedMapStoreFactoryBuilder { VersionedMapStoreFactoryBuilder strategy(StoreStrategy strategy); VersionedMapStoreFactoryBuilder stateBasedImmutableWhenCommitting(boolean transformToImmutable); VersionedMapStoreFactoryBuilder stateBasedSharingStrategy(SharingStrategy sharingStrategy); - VersionedMapStoreFactoryBuilder stateBasedHashProvider(ContinousHashProvider hashProvider); + VersionedMapStoreFactoryBuilder stateBasedHashProvider(ContinuousHashProvider hashProvider); VersionedMapStoreFactoryBuilder deltaTransactionStrategy(DeltaTransactionStrategy deltaStrategy); VersionedMapStoreFactory build(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java deleted file mode 100644 index a934d59e..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreImpl.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map; - -import tools.refinery.store.map.internal.*; - -import java.util.*; - -public class VersionedMapStoreImpl implements VersionedMapStore { - // Configuration - private final boolean immutableWhenCommitting; - - // Static data - protected final ContinousHashProvider hashProvider; - protected final V defaultValue; - - // Dynamic data - protected final Map> states = new HashMap<>(); - protected final Map, ImmutableNode> nodeCache; - protected long nextID = 0; - - public VersionedMapStoreImpl(ContinousHashProvider hashProvider, V defaultValue, - VersionedMapStoreConfiguration config) { - this.immutableWhenCommitting = config.isImmutableWhenCommitting(); - this.hashProvider = hashProvider; - this.defaultValue = defaultValue; - if (config.isSharedNodeCacheInStore()) { - nodeCache = new HashMap<>(); - } else { - nodeCache = null; - } - } - - private VersionedMapStoreImpl(ContinousHashProvider hashProvider, V defaultValue, - Map, ImmutableNode> nodeCache, VersionedMapStoreConfiguration config) { - this.immutableWhenCommitting = config.isImmutableWhenCommitting(); - this.hashProvider = hashProvider; - this.defaultValue = defaultValue; - this.nodeCache = nodeCache; - } - - public VersionedMapStoreImpl(ContinousHashProvider hashProvider, V defaultValue) { - this(hashProvider, defaultValue, new VersionedMapStoreConfiguration()); - } - - public static List> createSharedVersionedMapStores(int amount, - ContinousHashProvider hashProvider, V defaultValue, - VersionedMapStoreConfiguration config) { - List> result = new ArrayList<>(amount); - if (config.isSharedNodeCacheInStoreGroups()) { - Map, ImmutableNode> nodeCache; - if (config.isSharedNodeCacheInStore()) { - nodeCache = new HashMap<>(); - } else { - nodeCache = null; - } - for (int i = 0; i < amount; i++) { - result.add(new VersionedMapStoreImpl<>(hashProvider, defaultValue, nodeCache, config)); - } - } else { - for (int i = 0; i < amount; i++) { - result.add(new VersionedMapStoreImpl<>(hashProvider, defaultValue, config)); - } - } - return result; - } - - public static List> createSharedVersionedMapStores(int amount, - ContinousHashProvider hashProvider, V defaultValue) { - return createSharedVersionedMapStores(amount, hashProvider, defaultValue, new VersionedMapStoreConfiguration()); - } - - @Override - public synchronized Set getStates() { - return new HashSet<>(states.keySet()); - } - - @Override - public VersionedMap createMap() { - return new VersionedMapImpl<>(this, hashProvider, defaultValue); - } - - @Override - public VersionedMap createMap(long state) { - ImmutableNode data = revert(state); - return new VersionedMapImpl<>(this, hashProvider, defaultValue, data); - } - - public synchronized ImmutableNode revert(long state) { - if (states.containsKey(state)) { - return states.get(state); - } else { - ArrayList existingKeys = new ArrayList<>(states.keySet()); - Collections.sort(existingKeys); - throw new IllegalArgumentException("Store does not contain state " + state + "! Available states: " - + Arrays.toString(existingKeys.toArray())); - } - } - - public synchronized long commit(Node data, VersionedMapImpl mapToUpdateRoot) { - ImmutableNode immutable; - if (data != null) { - immutable = data.toImmutable(this.nodeCache); - } else { - immutable = null; - } - - if (nextID == Long.MAX_VALUE) - throw new IllegalStateException("Map store run out of Id-s"); - long id = nextID++; - this.states.put(id, immutable); - if (this.immutableWhenCommitting) { - mapToUpdateRoot.setRoot(immutable); - } - return id; - } - - @Override - public DiffCursor getDiffCursor(long fromState, long toState) { - VersionedMapImpl map1 = (VersionedMapImpl) createMap(fromState); - VersionedMapImpl map2 = (VersionedMapImpl) createMap(toState); - InOrderMapCursor cursor1 = new InOrderMapCursor<>(map1); - InOrderMapCursor cursor2 = new InOrderMapCursor<>(map2); - return new MapDiffCursor<>(this.defaultValue, cursor1, cursor2); - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java deleted file mode 100644 index fe490f46..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaBasedVersionedMapStoreFactory.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreDeltaImpl; -import tools.refinery.store.map.VersionedMapStoreFactory; -import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; - -import java.util.ArrayList; -import java.util.List; - -public class DeltaBasedVersionedMapStoreFactory implements VersionedMapStoreFactory { - private final V defaultValue; - private final boolean summarizeChanges; - - public DeltaBasedVersionedMapStoreFactory(V defaultValue, - VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy deltaTransactionStrategy) { - this.defaultValue = defaultValue; - this.summarizeChanges = deltaTransactionStrategy == VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy.SET; - } - - @Override - public VersionedMapStore createOne() { - return new VersionedMapStoreDeltaImpl<>(summarizeChanges, defaultValue); - } - - @Override - public List> createGroup(int amount) { - List> result = new ArrayList<>(amount); - for(int i=0; i(summarizeChanges,defaultValue)); - } - return result; - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java deleted file mode 100644 index cc9003e3..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/DeltaDiffCursor.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import tools.refinery.store.map.AnyVersionedMap; -import tools.refinery.store.map.DiffCursor; - -import java.util.Collections; -import java.util.List; -import java.util.Set; - -public class DeltaDiffCursor implements DiffCursor { - final List[]> backwardTransactions; - final List[]> forwardTransactions; - - boolean started; - /** - * Denotes the direction of traversal. False means backwards, true means - * forward. - */ - boolean direction; - int listIndex; - int arrayIndex; - - public DeltaDiffCursor(List[]> backwardTransactions, List[]> forwardTransactions) { - this.backwardTransactions = backwardTransactions; - this.forwardTransactions = forwardTransactions; - - if (!backwardTransactions.isEmpty()) { - direction = false; - listIndex = 0; - arrayIndex = backwardTransactions.get(listIndex).length - 1; - } else if (!forwardTransactions.isEmpty()) { - direction = true; - listIndex = forwardTransactions.size() - 1; - arrayIndex = 0; - } else { - direction = true; - listIndex = -1; - } - started = false; - } - - protected MapDelta getCurrentDelta() { - final List[]> list; - if (!direction) { - list = this.backwardTransactions; - } else { - list = this.forwardTransactions; - } - return list.get(listIndex)[arrayIndex]; - } - - @Override - public K getKey() { - return getCurrentDelta().getKey(); - } - - @Override - public V getValue() { - return getToValue(); - } - - @Override - public boolean isTerminated() { - return this.direction && listIndex == -1; - } - - - @Override - public boolean move() { - if(!started) { - started = true; - return !isTerminated(); - } else if (isTerminated()) { - return false; - } else { - if (this.direction) { - if (arrayIndex+1 < forwardTransactions.get(listIndex).length) { - arrayIndex++; - return true; - } else { - if (listIndex-1 >= 0) { - listIndex--; - arrayIndex = 0; - return true; - } else { - listIndex = -1; - return false; - } - } - } else { - if (arrayIndex > 0) { - arrayIndex--; - return true; - } else { - if (listIndex+1 < backwardTransactions.size()) { - listIndex++; - this.arrayIndex = backwardTransactions.get(listIndex).length - 1; - return true; - } else { - this.direction = true; - if (!this.forwardTransactions.isEmpty()) { - listIndex = forwardTransactions.size() - 1; - arrayIndex = 0; - return true; - } else { - listIndex = -1; - return false; - } - } - } - } - } - } - - @Override - public boolean isDirty() { - return false; - } - - @Override - public Set getDependingMaps() { - return Collections.emptySet(); - } - - @Override - public V getFromValue() { - if(this.direction) { - return getCurrentDelta().getOldValue(); - } else { - return getCurrentDelta().getNewValue(); - } - } - - @Override - public V getToValue() { - if(this.direction) { - return getCurrentDelta().getNewValue(); - } else { - return getCurrentDelta().getOldValue(); - } - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java deleted file mode 100644 index a357fbce..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/HashClash.java +++ /dev/null @@ -1,23 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -enum HashClash { - /** - * Not stuck. - */ - NONE, - - /** - * Clashed, next we should return the key of cursor 1. - */ - STUCK_CURSOR_1, - - /** - * Clashed, next we should return the key of cursor 2. - */ - STUCK_CURSOR_2 -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java deleted file mode 100644 index d052318f..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/ImmutableNode.java +++ /dev/null @@ -1,413 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import java.util.Arrays; -import java.util.Map; - -import tools.refinery.store.map.ContinousHashProvider; - -public class ImmutableNode extends Node { - /** - * Bitmap defining the stored key and values. - */ - final int dataMap; - /** - * Bitmap defining the positions of further nodes. - */ - final int nodeMap; - /** - * Stores Keys, Values, and sub-nodes. Structure: (K,V)*,NODE; NODES are stored - * backwards. - */ - final Object[] content; - - /** - * Hash code derived from immutable hash code - */ - final int precalculatedHash; - - private ImmutableNode(int dataMap, int nodeMap, Object[] content, int precalculatedHash) { - super(); - this.dataMap = dataMap; - this.nodeMap = nodeMap; - this.content = content; - this.precalculatedHash = precalculatedHash; - } - - /** - * Constructor that copies a mutable node to an immutable. - * - * @param node A mutable node. - * @param cache A cache of existing immutable nodes. It can be used to search - * and place reference immutable nodes. It can be null, if no cache - * available. - * @return an immutable version of the input node. - */ - static ImmutableNode constructImmutable(MutableNode node, Map, ImmutableNode> cache) { - // 1. try to return from cache - if (cache != null) { - ImmutableNode cachedResult = cache.get(node); - if (cachedResult != null) { - // 1.1 Already cached, return from cache. - return cachedResult; - } - } - - // 2. otherwise construct a new ImmutableNode - int size = 0; - for (int i = 0; i < node.content.length; i++) { - if (node.content[i] != null) { - size++; - } - } - - int datas = 0; - int nodes = 0; - int resultDataMap = 0; - int resultNodeMap = 0; - final Object[] resultContent = new Object[size]; - int bitPosition = 1; - for (int i = 0; i < FACTOR; i++) { - Object key = node.content[i * 2]; - if (key != null) { - resultDataMap |= bitPosition; - resultContent[datas * 2] = key; - resultContent[datas * 2 + 1] = node.content[i * 2 + 1]; - datas++; - } else { - @SuppressWarnings("unchecked") var subnode = (Node) node.content[i * 2 + 1]; - if (subnode != null) { - ImmutableNode immutableSubNode = subnode.toImmutable(cache); - resultNodeMap |= bitPosition; - resultContent[size - 1 - nodes] = immutableSubNode; - nodes++; - } - } - bitPosition <<= 1; - } - final int resultHash = node.hashCode(); - var newImmutable = new ImmutableNode(resultDataMap, resultNodeMap, resultContent, resultHash); - - // 3. save new immutable. - if (cache != null) { - cache.put(newImmutable, newImmutable); - } - return newImmutable; - } - - private int index(int bitmap, int bitpos) { - return Integer.bitCount(bitmap & (bitpos - 1)); - } - - @Override - public V getValue(K key, ContinousHashProvider hashProvider, V defaultValue, int hash, int depth) { - int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); - int bitposition = 1 << selectedHashFragment; - // If the key is stored as a data - if ((dataMap & bitposition) != 0) { - int keyIndex = 2 * index(dataMap, bitposition); - @SuppressWarnings("unchecked") K keyCandidate = (K) content[keyIndex]; - if (keyCandidate.equals(key)) { - @SuppressWarnings("unchecked") V value = (V) content[keyIndex + 1]; - return value; - } else { - return defaultValue; - } - } - // the key is stored as a node - else if ((nodeMap & bitposition) != 0) { - int keyIndex = content.length - 1 - index(nodeMap, bitposition); - @SuppressWarnings("unchecked") var subNode = (ImmutableNode) content[keyIndex]; - int newDepth = incrementDepth(depth); - int newHash = newHash(hashProvider, key, hash, newDepth); - return subNode.getValue(key, hashProvider, defaultValue, newHash, newDepth); - } - // the key is not stored at all - else { - return defaultValue; - } - } - - @Override - public Node putValue(K key, V value, OldValueBox oldValue, ContinousHashProvider hashProvider, V defaultValue, int hash, int depth) { - int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); - int bitPosition = 1 << selectedHashFragment; - if ((dataMap & bitPosition) != 0) { - int keyIndex = 2 * index(dataMap, bitPosition); - @SuppressWarnings("unchecked") K keyCandidate = (K) content[keyIndex]; - if (keyCandidate.equals(key)) { - if (value == defaultValue) { - // delete - MutableNode mutable = this.toMutable(); - return mutable.removeEntry(selectedHashFragment, oldValue); - } else if (value == content[keyIndex + 1]) { - // dont change - oldValue.setOldValue(value); - return this; - } else { - // update existing value - MutableNode mutable = this.toMutable(); - return mutable.updateValue(value, oldValue, selectedHashFragment); - } - } else { - if (value == defaultValue) { - // dont change - oldValue.setOldValue(defaultValue); - return this; - } else { - // add new key + value - MutableNode mutable = this.toMutable(); - return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); - } - } - } else if ((nodeMap & bitPosition) != 0) { - int keyIndex = content.length - 1 - index(nodeMap, bitPosition); - @SuppressWarnings("unchecked") var subNode = (ImmutableNode) content[keyIndex]; - int newDepth = incrementDepth(depth); - int newHash = newHash(hashProvider, key, hash, newDepth); - var newsubNode = subNode.putValue(key, value, oldValue, hashProvider, defaultValue, newHash, newDepth); - - if (subNode == newsubNode) { - // nothing changed - return this; - } else { - MutableNode mutable = toMutable(); - return mutable.updateWithSubNode(selectedHashFragment, newsubNode, - (value == null && defaultValue == null) || (value != null && value.equals(defaultValue))); - } - } else { - // add new key + value - MutableNode mutable = this.toMutable(); - return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); - } - } - - @Override - public long getSize() { - int result = Integer.bitCount(this.dataMap); - for (int subnodeIndex = 0; subnodeIndex < Integer.bitCount(this.nodeMap); subnodeIndex++) { - @SuppressWarnings("unchecked") var subnode = (ImmutableNode) this.content[this.content.length - 1 - subnodeIndex]; - result += subnode.getSize(); - } - return result; - } - - @Override - protected MutableNode toMutable() { - return new MutableNode<>(this); - } - - @Override - public ImmutableNode toImmutable(Map, ImmutableNode> cache) { - return this; - } - - @Override - protected MutableNode isMutable() { - return null; - } - - @SuppressWarnings("unchecked") - @Override - boolean moveToNext(MapCursor cursor) { - // 1. try to move to data - int datas = Integer.bitCount(this.dataMap); - if (cursor.dataIndex != MapCursor.INDEX_FINISH) { - int newDataIndex = cursor.dataIndex + 1; - if (newDataIndex < datas) { - cursor.dataIndex = newDataIndex; - cursor.key = (K) this.content[newDataIndex * 2]; - cursor.value = (V) this.content[newDataIndex * 2 + 1]; - return true; - } else { - cursor.dataIndex = MapCursor.INDEX_FINISH; - } - } - - // 2. look inside the subnodes - int nodes = Integer.bitCount(this.nodeMap); - if(cursor.nodeIndexStack.peek()==null) { - throw new IllegalStateException("Cursor moved to the next state when the state is empty."); - } - int newNodeIndex = cursor.nodeIndexStack.peek() + 1; - if (newNodeIndex < nodes) { - // 2.1 found next subnode, move down to the subnode - Node subnode = (Node) this.content[this.content.length - 1 - newNodeIndex]; - cursor.dataIndex = MapCursor.INDEX_START; - cursor.nodeIndexStack.pop(); - cursor.nodeIndexStack.push(newNodeIndex); - cursor.nodeIndexStack.push(MapCursor.INDEX_START); - cursor.nodeStack.push(subnode); - return subnode.moveToNext(cursor); - } else { - // 3. no subnode found, move up - cursor.nodeStack.pop(); - cursor.nodeIndexStack.pop(); - if (!cursor.nodeStack.isEmpty()) { - Node supernode = cursor.nodeStack.peek(); - return supernode.moveToNext(cursor); - } else { - cursor.key = null; - cursor.value = null; - return false; - } - } - } - - @Override - @SuppressWarnings("unchecked") - boolean moveToNextInorder(InOrderMapCursor cursor) { - if(cursor.nodeIndexStack.peek()==null) { - throw new IllegalStateException("Cursor moved to the next state when the state is empty."); - } - - int position = cursor.nodeIndexStack.peek(); - for (int index = position + 1; index < FACTOR; index++) { - final int mask = 1< subnode = (Node) this.content[this.content.length - 1 - index(nodeMap, mask)]; - cursor.nodeIndexStack.pop(); - cursor.nodeIndexStack.push(index); - cursor.nodeIndexStack.push(InOrderMapCursor.INDEX_START); - cursor.nodeStack.push(subnode); - - return subnode.moveToNextInorder(cursor); - } - } - - // nothing found - cursor.nodeStack.pop(); - cursor.nodeIndexStack.pop(); - if (!cursor.nodeStack.isEmpty()) { - Node supernode = cursor.nodeStack.peek(); - return supernode.moveToNextInorder(cursor); - } else { - cursor.key = null; - cursor.value = null; - return false; - } - } - - @Override - public void prettyPrint(StringBuilder builder, int depth, int code) { - builder.append("\t".repeat(Math.max(0, depth))); - if (code >= 0) { - builder.append(code); - builder.append(":"); - } - builder.append("Immutable("); - boolean hadContent = false; - int dataMask = 1; - for (int i = 0; i < FACTOR; i++) { - if ((dataMask & dataMap) != 0) { - if (hadContent) { - builder.append(","); - } - builder.append(i); - builder.append(":["); - builder.append(content[2 * index(dataMap, dataMask)].toString()); - builder.append("]->["); - builder.append(content[2 * index(dataMap, dataMask) + 1].toString()); - builder.append("]"); - hadContent = true; - } - dataMask <<= 1; - } - builder.append(")"); - int nodeMask = 1; - for (int i = 0; i < FACTOR; i++) { - if ((nodeMask & nodeMap) != 0) { - @SuppressWarnings("unchecked") Node subNode = (Node) content[content.length - 1 - index(nodeMap, nodeMask)]; - builder.append("\n"); - subNode.prettyPrint(builder, incrementDepth(depth), i); - } - nodeMask <<= 1; - } - } - - @Override - public void checkIntegrity(ContinousHashProvider hashProvider, V defaultValue, int depth) { - if (depth > 0) { - boolean orphaned = Integer.bitCount(dataMap) == 1 && nodeMap == 0; - if (orphaned) { - throw new IllegalStateException("Orphaned node! " + dataMap + ": " + content[0]); - } - } - // check the place of data - - // check subnodes - for (int i = 0; i < Integer.bitCount(nodeMap); i++) { - @SuppressWarnings("unchecked") var subnode = (Node) this.content[this.content.length - 1 - i]; - if (!(subnode instanceof ImmutableNode)) { - throw new IllegalStateException("Immutable node contains mutable subnodes!"); - } else { - subnode.checkIntegrity(hashProvider, defaultValue, incrementDepth(depth)); - } - } - } - - @Override - public int hashCode() { - return this.precalculatedHash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (obj instanceof ImmutableNode other) { - return precalculatedHash == other.precalculatedHash && dataMap == other.dataMap && nodeMap == other.nodeMap && Arrays.deepEquals(content, other.content); - } else if (obj instanceof MutableNode mutableObj) { - return ImmutableNode.compareImmutableMutable(this, mutableObj); - } else { - return false; - } - } - - public static boolean compareImmutableMutable(ImmutableNode immutable, MutableNode mutable) { - int datas = 0; - int nodes = 0; - final int immutableLength = immutable.content.length; - for (int i = 0; i < FACTOR; i++) { - Object key = mutable.content[i * 2]; - // For each key candidate - if (key != null) { - // Check whether a new Key-Value pair can fit into the immutable container - if (datas * 2 + nodes + 2 <= immutableLength) { - if (!immutable.content[datas * 2].equals(key) || !immutable.content[datas * 2 + 1].equals(mutable.content[i * 2 + 1])) { - return false; - } - } else return false; - datas++; - } else { - var mutableSubnode = (Node) mutable.content[i * 2 + 1]; - if (mutableSubnode != null) { - if (datas * 2 + nodes + 1 <= immutableLength) { - Object immutableSubNode = immutable.content[immutableLength - 1 - nodes]; - if (!mutableSubnode.equals(immutableSubNode)) { - return false; - } - nodes++; - } else { - return false; - } - } - } - } - - return datas * 2 + nodes == immutable.content.length; - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java deleted file mode 100644 index cb3f366f..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/InOrderMapCursor.java +++ /dev/null @@ -1,146 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import tools.refinery.store.map.AnyVersionedMap; -import tools.refinery.store.map.ContentHashCode; -import tools.refinery.store.map.Cursor; -import tools.refinery.store.map.VersionedMap; - -import java.util.*; - -public class InOrderMapCursor implements Cursor { - // Constants - static final int INDEX_START = -1; - - // Tree stack - ArrayDeque> nodeStack; - ArrayDeque nodeIndexStack; - - - // Values - K key; - V value; - - // Hash code for checking concurrent modifications - final VersionedMap map; - final int creationHash; - - public InOrderMapCursor(VersionedMapImpl map) { - // Initializing tree stack - super(); - this.nodeStack = new ArrayDeque<>(); - this.nodeIndexStack = new ArrayDeque<>(); - if (map.root != null) { - this.nodeStack.add(map.root); - this.nodeIndexStack.push(INDEX_START); - } - - // Initializing cache - this.key = null; - this.value = null; - - // Initializing state - this.map = map; - this.creationHash = map.contentHashCode(ContentHashCode.APPROXIMATE_FAST); - } - - public K getKey() { - return key; - } - - public V getValue() { - return value; - } - - public boolean isTerminated() { - return this.nodeStack.isEmpty(); - } - - public boolean move() { - if (isDirty()) { - throw new ConcurrentModificationException(); - } - if (!isTerminated()) { - var node = this.nodeStack.peek(); - if (node == null) { - throw new IllegalStateException("Cursor is not terminated but the current node is missing"); - } - boolean result = node.moveToNextInorder(this); - if (this.nodeIndexStack.size() != this.nodeStack.size()) { - throw new IllegalArgumentException("Node stack is corrupted by illegal moves!"); - } - return result; - } - return false; - } - - public boolean skipCurrentNode() { - nodeStack.pop(); - nodeIndexStack.pop(); - return move(); - } - - @Override - public boolean isDirty() { - return this.map.contentHashCode(ContentHashCode.APPROXIMATE_FAST) != this.creationHash; - } - - @Override - public Set getDependingMaps() { - return Set.of(this.map); - } - - public static boolean sameSubNode(InOrderMapCursor cursor1, InOrderMapCursor cursor2) { - Node nodeOfCursor1 = cursor1.nodeStack.peek(); - Node nodeOfCursor2 = cursor2.nodeStack.peek(); - return Objects.equals(nodeOfCursor1, nodeOfCursor2); - } - - /** - * Compares the state of two cursors started on two {@link VersionedMap} of the same - * {@link tools.refinery.store.map.VersionedMapStore}. - * @param Key type - * @param Value type - * @param cursor1 first cursor - * @param cursor2 second cursor - * @return Positive number if cursor 1 is behind, negative number if cursor 2 is behind, and 0 if they are at the - * same position. - */ - public static int comparePosition(InOrderMapCursor cursor1, InOrderMapCursor cursor2) { - // If the state does not determine the order, then compare @nodeIndexStack. - Iterator nodeIndexStack1 = cursor1.nodeIndexStack.descendingIterator(); - Iterator nodeIndexStack2 = cursor2.nodeIndexStack.descendingIterator(); - - while(nodeIndexStack1.hasNext() && nodeIndexStack2.hasNext()){ - final int index1 = nodeIndexStack1.next(); - final int index2 = nodeIndexStack2.next(); - if(index1 < index2) { - return 1; - } else if(index1 > index2) { - return -1; - } - } - - return 0; - } - - /** - * Compares the depth of two cursors started on @{@link VersionedMap} of the same - * {@link tools.refinery.store.map.VersionedMapStore}. - * @param Key type - * @param Value type - * @param cursor1 first cursor - * @param cursor2 second cursor - * @return Positive number if cursor 1 is deeper, negative number if cursor 2 is deeper, and 0 if they are at the - * same depth. - */ - public static int compareDepth(InOrderMapCursor cursor1, InOrderMapCursor cursor2) { - int d1 = cursor1.nodeIndexStack.size(); - int d2 = cursor2.nodeIndexStack.size(); - return Integer.compare(d1, d2); - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java deleted file mode 100644 index d1ab8bb1..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/IteratorAsCursor.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import java.util.*; -import java.util.Map.Entry; - -import tools.refinery.store.map.AnyVersionedMap; -import tools.refinery.store.map.Cursor; -import tools.refinery.store.map.VersionedMap; - -public class IteratorAsCursor implements Cursor { - final Iterator> iterator; - final VersionedMap source; - - private boolean terminated; - private K key; - private V value; - - public IteratorAsCursor(VersionedMap source, Map current) { - this.iterator = current.entrySet().iterator(); - this.source = source; - } - - @Override - public K getKey() { - return key; - } - - @Override - public V getValue() { - return value; - } - - @Override - public boolean isTerminated() { - return terminated; - } - - @Override - public boolean move() { - terminated = !iterator.hasNext(); - if (terminated) { - this.key = null; - this.value = null; - } else { - Entry next = iterator.next(); - this.key = next.getKey(); - this.value = next.getValue(); - } - return !terminated; - } - - @Override - public boolean isDirty() { - return false; - } - - @Override - public Set getDependingMaps() { - return Set.of(this.source); - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java deleted file mode 100644 index d42519b2..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapCursor.java +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import tools.refinery.store.map.AnyVersionedMap; -import tools.refinery.store.map.ContentHashCode; -import tools.refinery.store.map.Cursor; -import tools.refinery.store.map.VersionedMap; - -import java.util.ArrayDeque; -import java.util.ConcurrentModificationException; -import java.util.Set; - -public class MapCursor implements Cursor { - // Constants - static final int INDEX_START = -1; - static final int INDEX_FINISH = -2; - - // Tree stack - ArrayDeque> nodeStack; - ArrayDeque nodeIndexStack; - int dataIndex; - - // Values - K key; - V value; - - // Hash code for checking concurrent modifications - final VersionedMap map; - final int creationHash; - - public MapCursor(Node root, VersionedMap map) { - // Initializing tree stack - super(); - this.nodeStack = new ArrayDeque<>(); - this.nodeIndexStack = new ArrayDeque<>(); - if (root != null) { - this.nodeStack.add(root); - this.nodeIndexStack.push(INDEX_START); - } - - this.dataIndex = INDEX_START; - - // Initializing cache - this.key = null; - this.value = null; - - // Initializing state - this.map = map; - this.creationHash = map.contentHashCode(ContentHashCode.APPROXIMATE_FAST); - } - - public K getKey() { - return key; - } - - public V getValue() { - return value; - } - - public boolean isTerminated() { - return this.nodeStack.isEmpty(); - } - - public boolean move() { - if (isDirty()) { - throw new ConcurrentModificationException(); - } - if (!isTerminated()) { - var node = this.nodeStack.peek(); - if (node == null) { - throw new IllegalStateException("Cursor is not terminated but the current node is missing"); - } - boolean result = node.moveToNext(this); - if (this.nodeIndexStack.size() != this.nodeStack.size()) { - throw new IllegalArgumentException("Node stack is corrupted by illegal moves!"); - } - return result; - } - return false; - } - - @Override - public boolean isDirty() { - return this.map.contentHashCode(ContentHashCode.APPROXIMATE_FAST) != this.creationHash; - } - - @Override - public Set getDependingMaps() { - return Set.of(this.map); - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java deleted file mode 100644 index 2674236c..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDelta.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -public record MapDelta(K key, V oldValue, V newValue) { - public K getKey() { - return key; - } - - public V getOldValue() { - return oldValue; - } - - public V getNewValue() { - return newValue; - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java deleted file mode 100644 index fb1d5d2b..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapDiffCursor.java +++ /dev/null @@ -1,264 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import tools.refinery.store.map.AnyVersionedMap; -import tools.refinery.store.map.Cursor; -import tools.refinery.store.map.DiffCursor; - -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * A cursor representing the difference between two states of a map. - * - * @author Oszkar Semerath - */ -public class MapDiffCursor implements DiffCursor, Cursor { - private enum State { - /** - * initialized state. - */ - INIT, - /** - * Unstable state. - */ - MOVING_MOVING_SAME_KEY_SAME_VALUE, - /** - * Both cursors are moving, and they are on the same sub-node. - */ - MOVING_MOVING_SAME_NODE, - /** - * Both cursors are moving, cursor 1 is behind. - */ - MOVING_MOVING_BEHIND1, - /** - * Both cursors are moving, cursor 2 is behind. - */ - MOVING_MOVING_BEHIND2, - /** - * Both cursors are moving, cursor 1 is on the same key as cursor 2, values are different - */ - MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE, - /** - * Cursor 1 is moving, Cursor 2 is terminated. - */ - MOVING_TERMINATED, - /** - * Cursor 1 is terminated , Cursor 2 is moving. - */ - TERMINATED_MOVING, - /** - * Both cursors are terminated. - */ - TERMINATED_TERMINATED, - /** - * Both Cursors are moving, and they are on an incomparable position. - * It is resolved by showing Cursor 1. - */ - MOVING_MOVING_HASH1, - /** - * Both Cursors are moving, and they are on an incomparable position. - * It is resolved by showing Cursor 2. - */ - MOVING_MOVING_HASH2 - } - - /** - * Default nodeId representing missing elements. - */ - private final V defaultValue; - private final InOrderMapCursor cursor1; - private final InOrderMapCursor cursor2; - - // State - State state = State.INIT; - - // Values - private K key; - private V fromValue; - private V toValue; - - - public MapDiffCursor(V defaultValue, InOrderMapCursor cursor1, InOrderMapCursor cursor2) { - super(); - this.defaultValue = defaultValue; - this.cursor1 = cursor1; - this.cursor2 = cursor2; - } - - @Override - public K getKey() { - return key; - } - - @Override - public V getFromValue() { - return fromValue; - } - - @Override - public V getToValue() { - return toValue; - } - - @Override - public V getValue() { - return getToValue(); - } - - public boolean isTerminated() { - return this.state == State.TERMINATED_TERMINATED; - } - - @Override - public boolean isDirty() { - return this.cursor1.isDirty() || this.cursor2.isDirty(); - } - - @Override - public Set getDependingMaps() { - return Stream.concat(cursor1.getDependingMaps().stream(), cursor2.getDependingMaps().stream()).map(AnyVersionedMap.class::cast).collect(Collectors.toUnmodifiableSet()); - } - - private boolean isInStableState() { - return this.state != State.MOVING_MOVING_SAME_KEY_SAME_VALUE - && this.state != State.MOVING_MOVING_SAME_NODE && this.state != State.INIT; - } - - private boolean updateAndReturnWithResult() { - return switch (this.state) { - case INIT -> throw new IllegalStateException("DiffCursor terminated without starting!"); - case MOVING_MOVING_SAME_KEY_SAME_VALUE, MOVING_MOVING_SAME_NODE -> - throw new IllegalStateException("DiffCursor terminated in unstable state!"); - case MOVING_MOVING_BEHIND1, MOVING_TERMINATED, MOVING_MOVING_HASH1 -> { - this.key = this.cursor1.getKey(); - this.fromValue = this.cursor1.getValue(); - this.toValue = this.defaultValue; - yield true; - } - case MOVING_MOVING_BEHIND2, TERMINATED_MOVING, MOVING_MOVING_HASH2 -> { - this.key = this.cursor2.getKey(); - this.fromValue = this.defaultValue; - this.toValue = cursor2.getValue(); - yield true; - } - case MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE -> { - this.key = this.cursor1.getKey(); - this.fromValue = this.cursor1.getValue(); - this.toValue = this.cursor2.getValue(); - yield true; - } - case TERMINATED_TERMINATED -> { - this.key = null; - this.fromValue = null; - this.toValue = null; - yield false; - } - }; - } - - public boolean move() { - do { - this.state = moveOne(this.state); - } while (!isInStableState()); - return updateAndReturnWithResult(); - } - - private State moveOne(State currentState) { - return switch (currentState) { - case INIT, MOVING_MOVING_HASH2, MOVING_MOVING_SAME_KEY_SAME_VALUE, MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE -> { - boolean cursor1Moved = cursor1.move(); - boolean cursor2Moved = cursor2.move(); - yield recalculateStateAfterCursorMovement(cursor1Moved, cursor2Moved); - } - case MOVING_MOVING_SAME_NODE -> { - boolean cursor1Moved = cursor1.skipCurrentNode(); - boolean cursor2Moved = cursor2.skipCurrentNode(); - yield recalculateStateAfterCursorMovement(cursor1Moved, cursor2Moved); - } - case MOVING_MOVING_BEHIND1 -> { - boolean cursorMoved = cursor1.move(); - if (cursorMoved) { - yield recalculateStateBasedOnCursorRelation(); - } else { - yield State.TERMINATED_MOVING; - } - } - case MOVING_MOVING_BEHIND2 -> { - boolean cursorMoved = cursor2.move(); - if (cursorMoved) { - yield recalculateStateBasedOnCursorRelation(); - } else { - yield State.MOVING_TERMINATED; - } - } - case TERMINATED_MOVING -> { - boolean cursorMoved = cursor2.move(); - if (cursorMoved) { - yield State.TERMINATED_MOVING; - } else { - yield State.TERMINATED_TERMINATED; - } - } - case MOVING_TERMINATED -> { - boolean cursorMoved = cursor1.move(); - if (cursorMoved) { - yield State.MOVING_TERMINATED; - } else { - yield State.TERMINATED_TERMINATED; - } - } - case MOVING_MOVING_HASH1 -> State.MOVING_MOVING_HASH2; - case TERMINATED_TERMINATED -> throw new IllegalStateException("Trying to move while terminated!"); - }; - } - - private State recalculateStateAfterCursorMovement(boolean cursor1Moved, boolean cursor2Moved) { - if (cursor1Moved && cursor2Moved) { - return recalculateStateBasedOnCursorRelation(); - } else if (cursor1Moved) { - return State.MOVING_TERMINATED; - } else if (cursor2Moved) { - return State.TERMINATED_MOVING; - } else { - return State.TERMINATED_TERMINATED; - } - } - - private State recalculateStateBasedOnCursorRelation() { - final int relation = InOrderMapCursor.comparePosition(cursor1, cursor2); - - if (relation > 0) { - return State.MOVING_MOVING_BEHIND1; - } else if (relation < 0) { - return State.MOVING_MOVING_BEHIND2; - } - - if (InOrderMapCursor.sameSubNode(cursor1, cursor2)) { - return State.MOVING_MOVING_SAME_NODE; - } else if (Objects.equals(cursor1.getKey(), cursor2.getKey())) { - if (Objects.equals(cursor1.getValue(), cursor2.getValue())) { - return State.MOVING_MOVING_SAME_KEY_SAME_VALUE; - } else { - return State.MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE; - } - } - - final int depth = InOrderMapCursor.compareDepth(cursor1, cursor2); - - if (depth > 0) { - return State.MOVING_MOVING_BEHIND1; - } else if (depth < 0) { - return State.MOVING_MOVING_BEHIND2; - } else { - return State.MOVING_MOVING_HASH1; - } - - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java deleted file mode 100644 index d63522cd..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MapTransaction.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import java.util.Arrays; -import java.util.Objects; - -public record MapTransaction(MapDelta[] deltas, long version, MapTransaction parent) { - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + Arrays.hashCode(deltas); - result = prime * result + Objects.hash(parent, version); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - @SuppressWarnings("unchecked") - MapTransaction other = (MapTransaction) obj; - return Arrays.equals(deltas, other.deltas) && Objects.equals(parent, other.parent) && version == other.version; - } - - @Override - public String toString() { - return "MapTransaction " + version + " " + Arrays.toString(deltas); - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java deleted file mode 100644 index bb85deb9..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/MutableNode.java +++ /dev/null @@ -1,499 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import tools.refinery.store.map.ContinousHashProvider; - -import java.util.Arrays; -import java.util.Map; - -public class MutableNode extends Node { - int cachedHash; - protected boolean cachedHashValid; - protected Object[] content; - - protected MutableNode() { - this.content = new Object[2 * FACTOR]; - invalidateHash(); - } - - public static MutableNode initialize(K key, V value, ContinousHashProvider hashProvider, V defaultValue) { - if (value == defaultValue) { - return null; - } else { - int hash = hashProvider.getHash(key, 0); - int fragment = hashFragment(hash, 0); - MutableNode res = new MutableNode<>(); - res.content[2 * fragment] = key; - res.content[2 * fragment + 1] = value; - res.invalidateHash(); - return res; - } - } - - /** - * Constructs a {@link MutableNode} as a copy of an {@link ImmutableNode} - * - * @param node to be transformed - */ - protected MutableNode(ImmutableNode node) { - this.content = new Object[2 * FACTOR]; - int dataUsed = 0; - int nodeUsed = 0; - for (int i = 0; i < FACTOR; i++) { - int bitPosition = 1 << i; - if ((node.dataMap & bitPosition) != 0) { - content[2 * i] = node.content[dataUsed * 2]; - content[2 * i + 1] = node.content[dataUsed * 2 + 1]; - dataUsed++; - } else if ((node.nodeMap & bitPosition) != 0) { - content[2 * i + 1] = node.content[node.content.length - 1 - nodeUsed]; - nodeUsed++; - } - } - this.cachedHashValid = false; - } - - @Override - public V getValue(K key, ContinousHashProvider hashProvider, V defaultValue, int hash, int depth) { - int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); - @SuppressWarnings("unchecked") K keyCandidate = (K) this.content[2 * selectedHashFragment]; - if (keyCandidate != null) { - if (keyCandidate.equals(key)) { - @SuppressWarnings("unchecked") V value = (V) this.content[2 * selectedHashFragment + 1]; - return value; - } else { - return defaultValue; - } - } else { - @SuppressWarnings("unchecked") var nodeCandidate = (Node) content[2 * selectedHashFragment + 1]; - if (nodeCandidate != null) { - int newDepth = incrementDepth(depth); - int newHash = newHash(hashProvider, key, hash, newDepth); - return nodeCandidate.getValue(key, hashProvider, defaultValue, newHash, newDepth); - } else { - return defaultValue; - } - } - } - - @Override - public Node putValue(K key, V value, OldValueBox oldValueBox, ContinousHashProvider hashProvider, V defaultValue, int hash, int depth) { - int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); - @SuppressWarnings("unchecked") K keyCandidate = (K) content[2 * selectedHashFragment]; - if (keyCandidate != null) { - // If it has key - if (keyCandidate.equals(key)) { - // The key is equals to an existing key -> update entry - if (value == defaultValue) { - return removeEntry(selectedHashFragment, oldValueBox); - } else { - return updateValue(value, oldValueBox, selectedHashFragment); - } - } else { - // The key is not equivalent to an existing key on the same hash bin - // -> split entry if it is necessary - if (value == defaultValue) { - // Value is default -> do not need to add new node - oldValueBox.setOldValue(defaultValue); - return this; - } else { - // Value is not default -> Split entry data to a new node - oldValueBox.setOldValue(defaultValue); - return moveDownAndSplit(hashProvider, key, value, keyCandidate, hash, depth, selectedHashFragment); - } - } - } - // If it does not have key, check for value - @SuppressWarnings("unchecked") var nodeCandidate = (Node) content[2 * selectedHashFragment + 1]; - if (nodeCandidate != null) { - // If it has value, it is a sub-node -> update that - int newDepth = incrementDepth(depth); - var newNode = nodeCandidate.putValue(key, value, oldValueBox, hashProvider, defaultValue, newHash(hashProvider, key, hash, newDepth), newDepth); - return updateWithSubNode(selectedHashFragment, newNode, (value == null && defaultValue == null) || (value != null && value.equals(defaultValue))); - } else { - // If it does not have value, put it in the empty place - if (value == defaultValue) { - // don't need to add new key-value pair - oldValueBox.setOldValue(defaultValue); - return this; - } else { - return addEntry(key, value, oldValueBox, selectedHashFragment, defaultValue); - } - } - } - - private Node addEntry(K key, V value, OldValueBox oldValueBox, int selectedHashFragment, V defaultValue) { - content[2 * selectedHashFragment] = key; - oldValueBox.setOldValue(defaultValue); - content[2 * selectedHashFragment + 1] = value; - invalidateHash(); - return this; - } - - /** - * Updates an entry in a selected hash-fragment to a non-default value. - * - * @param value new value - * @param selectedHashFragment position of the value - * @return updated node - */ - @SuppressWarnings("unchecked") - Node updateValue(V value, OldValueBox oldValue, int selectedHashFragment) { - oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]); - content[2 * selectedHashFragment + 1] = value; - invalidateHash(); - return this; - } - - /** - * Updates an entry in a selected hash-fragment with a subtree. - * - * @param selectedHashFragment position of the value - * @param newNode the subtree - * @return updated node - */ - Node updateWithSubNode(int selectedHashFragment, Node newNode, boolean deletionHappened) { - if (deletionHappened) { - if (newNode == null) { - // Check whether this node become empty - content[2 * selectedHashFragment + 1] = null; // i.e. the new node - if (hasContent()) { - invalidateHash(); - return this; - } else { - return null; - } - } else { - // check whether newNode is orphan - MutableNode immutableNewNode = newNode.isMutable(); - if (immutableNewNode != null) { - int orphaned = immutableNewNode.isOrphaned(); - if (orphaned >= 0) { - // orphan sub-node data is replaced with data - content[2 * selectedHashFragment] = immutableNewNode.content[orphaned * 2]; - content[2 * selectedHashFragment + 1] = immutableNewNode.content[orphaned * 2 + 1]; - invalidateHash(); - return this; - } - } - } - } - // normal behaviour - content[2 * selectedHashFragment + 1] = newNode; - invalidateHash(); - return this; - } - - private boolean hasContent() { - for (Object element : this.content) { - if (element != null) return true; - } - return false; - } - - @Override - protected MutableNode isMutable() { - return this; - } - - protected int isOrphaned() { - int dataFound = -2; - for (int i = 0; i < FACTOR; i++) { - if (content[i * 2] != null) { - if (dataFound >= 0) { - return -1; - } else { - dataFound = i; - } - } else if (content[i * 2 + 1] != null) { - return -3; - } - } - return dataFound; - } - - @SuppressWarnings("unchecked") - private Node moveDownAndSplit(ContinousHashProvider hashProvider, K newKey, V newValue, K previousKey, int hashOfNewKey, int depth, int selectedHashFragmentOfCurrentDepth) { - V previousValue = (V) content[2 * selectedHashFragmentOfCurrentDepth + 1]; - - MutableNode newSubNode = newNodeWithTwoEntries(hashProvider, previousKey, previousValue, hashProvider.getHash(previousKey, hashDepth(depth)), newKey, newValue, hashOfNewKey, incrementDepth(depth)); - - content[2 * selectedHashFragmentOfCurrentDepth] = null; - content[2 * selectedHashFragmentOfCurrentDepth + 1] = newSubNode; - invalidateHash(); - return this; - } - - // Pass everything as parameters for performance. - @SuppressWarnings("squid:S107") - private MutableNode newNodeWithTwoEntries(ContinousHashProvider hashProvider, K key1, V value1, int oldHash1, K key2, V value2, int oldHash2, int newDepth) { - int newHash1 = newHash(hashProvider, key1, oldHash1, newDepth); - int newHash2 = newHash(hashProvider, key2, oldHash2, newDepth); - int newFragment1 = hashFragment(newHash1, shiftDepth(newDepth)); - int newFragment2 = hashFragment(newHash2, shiftDepth(newDepth)); - - MutableNode subNode = new MutableNode<>(); - if (newFragment1 != newFragment2) { - subNode.content[newFragment1 * 2] = key1; - subNode.content[newFragment1 * 2 + 1] = value1; - - subNode.content[newFragment2 * 2] = key2; - subNode.content[newFragment2 * 2 + 1] = value2; - } else { - MutableNode subSubNode = newNodeWithTwoEntries(hashProvider, key1, value1, newHash1, key2, value2, newHash2, incrementDepth(newDepth)); - subNode.content[newFragment1 * 2 + 1] = subSubNode; - } - subNode.invalidateHash(); - return subNode; - } - - @SuppressWarnings("unchecked") - Node removeEntry(int selectedHashFragment, OldValueBox oldValue) { - content[2 * selectedHashFragment] = null; - oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]); - content[2 * selectedHashFragment + 1] = null; - if (hasContent()) { - invalidateHash(); - return this; - } else { - return null; - } - } - - @SuppressWarnings("unchecked") - @Override - public long getSize() { - int size = 0; - for (int i = 0; i < FACTOR; i++) { - if (content[i * 2] != null) { - size++; - } else { - Node nodeCandidate = (Node) content[i * 2 + 1]; - if (nodeCandidate != null) { - size += nodeCandidate.getSize(); - } - } - } - return size; - } - - @Override - protected MutableNode toMutable() { - return this; - } - - @Override - public ImmutableNode toImmutable(Map, ImmutableNode> cache) { - return ImmutableNode.constructImmutable(this, cache); - } - - @SuppressWarnings("unchecked") - @Override - boolean moveToNext(MapCursor cursor) { - // 1. try to move to data - if (cursor.dataIndex != MapCursor.INDEX_FINISH) { - for (int index = cursor.dataIndex + 1; index < FACTOR; index++) { - if (this.content[index * 2] != null) { - // 1.1 found next data - cursor.dataIndex = index; - cursor.key = (K) this.content[index * 2]; - cursor.value = (V) this.content[index * 2 + 1]; - return true; - } - } - cursor.dataIndex = MapCursor.INDEX_FINISH; - } - - // 2. look inside the sub-nodes - if(cursor.nodeIndexStack.peek()==null) { - throw new IllegalStateException("Cursor moved to the next state when the state is empty."); - } - for (int index = cursor.nodeIndexStack.peek() + 1; index < FACTOR; index++) { - if (this.content[index * 2] == null && this.content[index * 2 + 1] != null) { - // 2.1 found next sub-node, move down to the sub-node - Node subnode = (Node) this.content[index * 2 + 1]; - - cursor.dataIndex = MapCursor.INDEX_START; - cursor.nodeIndexStack.pop(); - cursor.nodeIndexStack.push(index); - cursor.nodeIndexStack.push(MapCursor.INDEX_START); - cursor.nodeStack.push(subnode); - - return subnode.moveToNext(cursor); - } - } - // 3. no sub-node found, move up - cursor.nodeStack.pop(); - cursor.nodeIndexStack.pop(); - if (!cursor.nodeStack.isEmpty()) { - Node supernode = cursor.nodeStack.peek(); - return supernode.moveToNext(cursor); - } else { - cursor.key = null; - cursor.value = null; - return false; - } - } - - @Override - @SuppressWarnings("unchecked") - boolean moveToNextInorder(InOrderMapCursor cursor) { - if(cursor.nodeIndexStack.peek()==null || cursor.nodeStack.peek()==null) { - throw new IllegalStateException("Cursor moved to the next state when the state is empty."); - } - - int position = cursor.nodeIndexStack.peek(); - - for (int index = position + 1; index < FACTOR; index++) { - // data found - if (this.content[index * 2] != null) { - cursor.nodeIndexStack.pop(); - cursor.nodeIndexStack.push(index); - - cursor.key = (K) this.content[index * 2]; - cursor.value = (V) this.content[index * 2 + 1]; - return true; - } else if (this.content[index * 2 +1] != null) { - // sub-node found - Node subnode = (Node) this.content[index * 2 +1]; - cursor.nodeIndexStack.pop(); - cursor.nodeIndexStack.push(index); - cursor.nodeIndexStack.push(InOrderMapCursor.INDEX_START); - cursor.nodeStack.push(subnode); - - return subnode.moveToNextInorder(cursor); - } - } - - // nothing found - cursor.nodeStack.pop(); - cursor.nodeIndexStack.pop(); - if (!cursor.nodeStack.isEmpty()) { - Node supernode = cursor.nodeStack.peek(); - return supernode.moveToNextInorder(cursor); - } else { - cursor.key = null; - cursor.value = null; - return false; - } - } - - @Override - public void prettyPrint(StringBuilder builder, int depth, int code) { - builder.append("\t".repeat(Math.max(0, depth))); - if (code >= 0) { - builder.append(code); - builder.append(":"); - } - builder.append("Mutable("); - // print content - boolean hadContent = false; - for (int i = 0; i < FACTOR; i++) { - if (content[2 * i] != null) { - if (hadContent) { - builder.append(","); - } - builder.append(i); - builder.append(":["); - builder.append(content[2 * i].toString()); - builder.append("]->["); - builder.append(content[2 * i + 1].toString()); - builder.append("]"); - hadContent = true; - } - } - builder.append(")"); - // print sub-nodes - for (int i = 0; i < FACTOR; i++) { - if (content[2 * i] == null && content[2 * i + 1] != null) { - @SuppressWarnings("unchecked") Node subNode = (Node) content[2 * i + 1]; - builder.append("\n"); - subNode.prettyPrint(builder, incrementDepth(depth), i); - } - } - } - - @Override - public void checkIntegrity(ContinousHashProvider hashProvider, V defaultValue, int depth) { - // check for orphan nodes - if (depth > 0) { - int orphaned = isOrphaned(); - if (orphaned >= 0) { - throw new IllegalStateException("Orphaned node! " + orphaned + ": " + content[2 * orphaned]); - } - } - // check the place of data - for (int i = 0; i < FACTOR; i++) { - if (this.content[2 * i] != null) { - @SuppressWarnings("unchecked") K key = (K) this.content[2 * i]; - @SuppressWarnings("unchecked") V value = (V) this.content[2 * i + 1]; - - if (value == defaultValue) { - throw new IllegalStateException("Node contains default value!"); - } - int hashCode = hashProvider.getHash(key, hashDepth(depth)); - int shiftDepth = shiftDepth(depth); - int selectedHashFragment = hashFragment(hashCode, shiftDepth); - if (i != selectedHashFragment) { - throw new IllegalStateException("Key " + key + " with hash code " + hashCode + " is in bad place! Fragment=" + selectedHashFragment + ", Place=" + i); - } - } - } - // check sub-nodes - for (int i = 0; i < FACTOR; i++) { - if (this.content[2 * i + 1] != null && this.content[2 * i] == null) { - @SuppressWarnings("unchecked") var subNode = (Node) this.content[2 * i + 1]; - subNode.checkIntegrity(hashProvider, defaultValue, incrementDepth(depth)); - } - } - // check the hash - if (cachedHashValid) { - int oldHash = this.cachedHash; - invalidateHash(); - int newHash = hashCode(); - if (oldHash != newHash) { - throw new IllegalStateException("Hash code was not up to date! (old=" + oldHash + ",new=" + newHash + ")"); - } - } - } - - protected void invalidateHash() { - this.cachedHashValid = false; - } - - @Override - public int hashCode() { - if (!this.cachedHashValid) { - this.cachedHash = Arrays.hashCode(content); - this.cachedHashValid = true; - } - return this.cachedHash; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) return true; - if (obj == null) return false; - if (obj instanceof MutableNode mutableObj) { - if (obj.hashCode() != this.hashCode()) { - return false; - } else { - for (int i = 0; i < FACTOR * 2; i++) { - Object thisContent = this.content[i]; - if (thisContent != null && !thisContent.equals(mutableObj.content[i])) { - return false; - } - } - return true; - } - } else if (obj instanceof ImmutableNode immutableObj) { - return ImmutableNode.compareImmutableMutable(immutableObj, this); - } else { - return false; - } - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java deleted file mode 100644 index 4b44f760..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/Node.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import java.util.Map; - -import tools.refinery.store.map.ContinousHashProvider; - -public abstract class Node { - public static final int BRANCHING_FACTOR_BITS = 5; - public static final int FACTOR = 1 << BRANCHING_FACTOR_BITS; - protected static final int NUMBER_OF_FACTORS = Integer.SIZE / BRANCHING_FACTOR_BITS; - protected static final int FACTOR_MASK = FACTOR - 1; - public static final int EFFECTIVE_BITS = BRANCHING_FACTOR_BITS * NUMBER_OF_FACTORS; - public static final int FACTOR_SELECTION_BITS = 32 - Integer.numberOfLeadingZeros(NUMBER_OF_FACTORS); - public static final int FACTOR_SELECTION_MASK = (1 << FACTOR_SELECTION_BITS) - 1; - public static final int INCREMENT_BIG_STEP = (1 << FACTOR_SELECTION_BITS) - NUMBER_OF_FACTORS; - - /** - * Increments the depth of the search in the tree. The depth parameter has two - * components: the least few bits selects the fragment of the hashcode, the - * other part selects the continuous hash. - * - * @param depth parameter encoding the fragment and the depth - * @return new depth. - */ - protected int incrementDepth(int depth) { - int newDepth = depth + 1; - if ((newDepth & FACTOR_SELECTION_MASK) == NUMBER_OF_FACTORS) { - newDepth += INCREMENT_BIG_STEP; - } - return newDepth; - } - - /** - * Calculates the index for the continuous hash. - * - * @param depth The depth of the node in the tree. - * @return The index of the continuous hash. - */ - protected static int hashDepth(int depth) { - return depth >> FACTOR_SELECTION_BITS; - } - - /** - * Calculates the which segment of a single hash should be used. - * - * @param depth The depth of the node in the tree. - * @return The segment of a hash code. - */ - protected static int shiftDepth(int depth) { - return depth & FACTOR_SELECTION_MASK; - } - - /** - * Selects a segments from a complete hash for a given depth. - * - * @param hash A complete hash. - * @param shiftDepth The index of the segment. - * @return The segment as an integer. - */ - protected static int hashFragment(int hash, int shiftDepth) { - if (shiftDepth < 0 || Node.NUMBER_OF_FACTORS < shiftDepth) - throw new IllegalArgumentException("Invalid shift depth! valid interval=[0;5], input=" + shiftDepth); - return (hash >>> shiftDepth * BRANCHING_FACTOR_BITS) & FACTOR_MASK; - } - - /** - * Returns the hash code for a given depth. It may calculate new hash code, or - * reuse a hash code calculated for depth-1. - * - * @param key The key. - * @param hash Hash code for depth-1 - * @param depth The depth. - * @return The new hash code. - */ - protected int newHash(final ContinousHashProvider hashProvider, K key, int hash, int depth) { - final int shiftDepth = shiftDepth(depth); - if (shiftDepth == 0) { - final int hashDepth = hashDepth(depth); - if (hashDepth >= ContinousHashProvider.MAX_PRACTICAL_DEPTH) { - throw new IllegalArgumentException( - "Key " + key + " have the clashing hashcode over the practical depth limitation (" - + ContinousHashProvider.MAX_PRACTICAL_DEPTH + ")!"); - } - return hashProvider.getHash(key, hashDepth); - } else { - return hash; - } - } - - public abstract V getValue(K key, ContinousHashProvider hashProvider, V defaultValue, int hash, - int depth); - - public abstract Node putValue(K key, V value, OldValueBox old, - ContinousHashProvider hashProvider, V defaultValue, int hash, int depth); - - public abstract long getSize(); - - abstract MutableNode toMutable(); - - public abstract ImmutableNode toImmutable(Map, ImmutableNode> cache); - - protected abstract MutableNode isMutable(); - - /** - * Moves a {@link MapCursor} to its next position. - * - * @param cursor the cursor - * @return Whether there was a next value to move on. - */ - abstract boolean moveToNext(MapCursor cursor); - abstract boolean moveToNextInorder(InOrderMapCursor cursor); - - ///////// FOR printing - public abstract void prettyPrint(StringBuilder builder, int depth, int code); - - - @Override - public String toString() { - StringBuilder stringBuilder = new StringBuilder(); - prettyPrint(stringBuilder, 0, -1); - return stringBuilder.toString(); - } - - public void checkIntegrity(ContinousHashProvider hashProvider, V defaultValue, int depth) { - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/OldValueBox.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/OldValueBox.java deleted file mode 100644 index 354af51d..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/OldValueBox.java +++ /dev/null @@ -1,24 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -public class OldValueBox{ - V oldValue; - boolean isSet = false; - - public V getOldValue() { - if(!isSet) throw new IllegalStateException(); - isSet = false; - return oldValue; - } - - public void setOldValue(V ouldValue) { - if(isSet) throw new IllegalStateException(); - this.oldValue = ouldValue; - isSet = true; - } - -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java deleted file mode 100644 index 1c3ab27b..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/StateBasedVersionedMapStoreFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import tools.refinery.store.map.*; - -import java.util.List; - -public class StateBasedVersionedMapStoreFactory implements VersionedMapStoreFactory { - private final V defaultValue; - private final ContinousHashProvider continousHashProvider; - private final VersionedMapStoreConfiguration config; - - public StateBasedVersionedMapStoreFactory(V defaultValue, Boolean transformToImmutable, VersionedMapStoreFactoryBuilder.SharingStrategy sharingStrategy, ContinousHashProvider continousHashProvider) { - this.defaultValue = defaultValue; - this.continousHashProvider = continousHashProvider; - - this.config = new VersionedMapStoreConfiguration( - transformToImmutable, - sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE || sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP, - sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP); - } - - @Override - public VersionedMapStore createOne() { - return new VersionedMapStoreImpl<>(continousHashProvider, defaultValue, config); - - } - - @Override - public List> createGroup(int amount) { - return VersionedMapStoreImpl.createSharedVersionedMapStores(amount, continousHashProvider, defaultValue, - config); - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java deleted file mode 100644 index ba59cfef..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaArrayStore.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import java.util.ArrayList; -import java.util.List; - -public class UncommittedDeltaArrayStore implements UncommittedDeltaStore { - final List> uncommittedOldValues = new ArrayList<>(); - - @Override - public void processChange(K key, V oldValue, V newValue) { - uncommittedOldValues.add(new MapDelta<>(key, oldValue, newValue)); - } - - @Override - public MapDelta[] extractDeltas() { - if (uncommittedOldValues.isEmpty()) { - return null; - } else { - @SuppressWarnings("unchecked") - MapDelta[] result = uncommittedOldValues.toArray(new MapDelta[0]); - return result; - } - } - - @Override - public MapDelta[] extractAndDeleteDeltas() { - MapDelta[] res = extractDeltas(); - this.uncommittedOldValues.clear(); - return res; - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java deleted file mode 100644 index 61a34351..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaMapStore.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import java.util.*; -import java.util.Map.Entry; - -import tools.refinery.store.map.VersionedMap; - -public class UncommittedDeltaMapStore implements UncommittedDeltaStore { - final VersionedMap source; - final Map uncommittedOldValues = new HashMap<>(); - - public UncommittedDeltaMapStore(VersionedMap source) { - this.source = source; - } - - @Override - public void processChange(K key, V oldValue, V newValue) { - if(!uncommittedOldValues.containsKey(key)) { - this.uncommittedOldValues.put(key,oldValue); - } - } - - @Override - public MapDelta[] extractDeltas() { - if (uncommittedOldValues.isEmpty()) { - return null; - } else { - @SuppressWarnings("unchecked") - MapDelta[] deltas = new MapDelta[uncommittedOldValues.size()]; - int i = 0; - for (Entry entry : uncommittedOldValues.entrySet()) { - final K key = entry.getKey(); - final V oldValue = entry.getValue(); - final V newValue = source.get(key); - deltas[i++] = new MapDelta<>(key, oldValue, newValue); - } - - return deltas; - } - } - - @Override - public MapDelta[] extractAndDeleteDeltas() { - MapDelta[] res = extractDeltas(); - this.uncommittedOldValues.clear(); - return res; - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java deleted file mode 100644 index 438b5561..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/UncommittedDeltaStore.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -public interface UncommittedDeltaStore { - void processChange(K key, V oldValue, V newValue); - - MapDelta[] extractDeltas(); - - MapDelta[] extractAndDeleteDeltas(); - - default void checkIntegrity() { - MapDelta[] extractedDeltas = extractDeltas(); - if(extractedDeltas != null) { - for(var uncommittedOldValue : extractedDeltas) { - if(uncommittedOldValue == null) { - throw new IllegalArgumentException("Null entry in deltas!"); - } - if(uncommittedOldValue.getKey() == null) { - throw new IllegalStateException("Null key in deltas!"); - } - } - } - } - -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java deleted file mode 100644 index ae47feda..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapDeltaImpl.java +++ /dev/null @@ -1,219 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import java.util.*; - -import tools.refinery.store.map.*; - -public class VersionedMapDeltaImpl implements VersionedMap { - protected final VersionedMapStoreDeltaImpl store; - - final Map current; - - final UncommittedDeltaStore uncommittedStore; - MapTransaction previous; - - protected final V defaultValue; - - public VersionedMapDeltaImpl(VersionedMapStoreDeltaImpl store, boolean summarizeChanges, V defaultValue) { - this.store = store; - this.defaultValue = defaultValue; - - current = new HashMap<>(); - if (summarizeChanges) { - this.uncommittedStore = new UncommittedDeltaMapStore<>(this); - } else { - this.uncommittedStore = new UncommittedDeltaArrayStore<>(); - } - } - - @Override - public V getDefaultValue() { - return defaultValue; - } - - @Override - public long commit() { - MapDelta[] deltas = uncommittedStore.extractAndDeleteDeltas(); - long[] versionContainer = new long[1]; - this.previous = this.store.appendTransaction(deltas, previous, versionContainer); - return versionContainer[0]; - } - - @Override - public void restore(long state) { - // 1. restore uncommitted states - MapDelta[] uncommitted = this.uncommittedStore.extractAndDeleteDeltas(); - if (uncommitted != null) { - backward(uncommitted); - } - - // 2. get common ancestor - final MapTransaction parent; - List[]> forward = new ArrayList<>(); - if (this.previous == null) { - parent = this.store.getPath(state, forward); - this.forward(forward); - } else { - List[]> backward = new ArrayList<>(); - parent = this.store.getPath(this.previous.version(), state, backward, forward); - this.backward(backward); - this.forward(forward); - } - this.previous = parent; - } - - protected void forward(List[]> changes) { - for (int i = changes.size() - 1; i >= 0; i--) { - forward(changes.get(i)); - } - } - - protected void backward(List[]> changes) { - for (int i = 0; i < changes.size(); i++) { - backward(changes.get(i)); - } - } - - protected void forward(MapDelta[] changes) { - for (int i = 0; i < changes.length; i++) { - final MapDelta change = changes[i]; - K key = change.getKey(); - V newValue = change.getNewValue(); - - if(newValue == defaultValue) { - current.remove(key); - } else { - current.put(key,newValue); - } - } - } - - protected void backward(MapDelta[] changes) { - for (int i = changes.length - 1; i >= 0; i--) { - final MapDelta change = changes[i]; - K key = change.getKey(); - V oldValue = change.oldValue(); - - if(oldValue == defaultValue) { - current.remove(key); - } else { - current.put(key,oldValue); - } - } - } - - @Override - public V get(K key) { - return current.getOrDefault(key, defaultValue); - } - - @Override - public Cursor getAll() { - return new IteratorAsCursor<>(this, current); - } - - @Override - public V put(K key, V value) { - final V oldValue; - if (Objects.equals(value, defaultValue)) { - final V res = current.remove(key); - if (res == null) { - // no changes: default > default - oldValue = defaultValue; - } else { - oldValue = res; - } - } else { - final var mapValue = current.put(key, value); - if (mapValue == null) { - oldValue = defaultValue; - } else { - oldValue = mapValue; - } - } - if(!Objects.equals(oldValue,value)) { - uncommittedStore.processChange(key, oldValue, value); - } - return oldValue; - } - - @Override - public void putAll(Cursor cursor) { - if (cursor.getDependingMaps().contains(this)) { - List keys = new ArrayList<>(); - List values = new ArrayList<>(); - while (cursor.move()) { - keys.add(cursor.getKey()); - values.add(cursor.getValue()); - } - for (int i = 0; i < keys.size(); i++) { - this.put(keys.get(i), values.get(i)); - } - } else { - while (cursor.move()) { - this.put(cursor.getKey(), cursor.getValue()); - } - } - } - - @Override - public long getSize() { - return current.size(); - } - - @Override - public DiffCursor getDiffCursor(long state) { - MapDelta[] backward = this.uncommittedStore.extractDeltas(); - List[]> backwardTransactions = new ArrayList<>(); - List[]> forwardTransactions = new ArrayList<>(); - - if (backward != null) { - backwardTransactions.add(backward); - } - - if (this.previous != null) { - store.getPath(this.previous.version(), state, backwardTransactions, forwardTransactions); - } else { - store.getPath(state, forwardTransactions); - } - - return new DeltaDiffCursor<>(backwardTransactions, forwardTransactions); - } - - @Override - public int contentHashCode(ContentHashCode mode) { - return this.current.hashCode(); - } - - @Override - public boolean contentEquals(AnyVersionedMap other) { - if (other instanceof VersionedMapDeltaImpl versioned) { - if (versioned == this) { - return true; - } else { - return Objects.equals(this.defaultValue, versioned.defaultValue) && Objects.equals(this.current, versioned.current); - } - } else { - throw new UnsupportedOperationException("Comparing different map implementations is ineffective."); - } - } - - @Override - public void checkIntegrity() { - this.uncommittedStore.checkIntegrity(); - - for (var entry : this.current.entrySet()) { - var value = entry.getValue(); - if (value == this.defaultValue) { - throw new IllegalStateException("Default value stored in map!"); - } else if (value == null) { - throw new IllegalStateException("null value stored in map!"); - } - } - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java deleted file mode 100644 index c107f7e0..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapImpl.java +++ /dev/null @@ -1,171 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.map.internal; - -import tools.refinery.store.map.*; - -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Objects; - -/** - * Not threadSafe in itself - * - * @param - * @param - * @author Oszkar Semerath - */ -public class VersionedMapImpl implements VersionedMap { - protected final VersionedMapStoreImpl store; - - protected final ContinousHashProvider hashProvider; - protected final V defaultValue; - protected Node root; - - private final OldValueBox oldValueBox = new OldValueBox<>(); - - public VersionedMapImpl( - VersionedMapStoreImpl store, - ContinousHashProvider hashProvider, - V defaultValue) { - this.store = store; - this.hashProvider = hashProvider; - this.defaultValue = defaultValue; - this.root = null; - } - - public VersionedMapImpl( - VersionedMapStoreImpl store, - ContinousHashProvider hashProvider, - V defaultValue, Node data) { - this.store = store; - this.hashProvider = hashProvider; - this.defaultValue = defaultValue; - this.root = data; - } - - @Override - public V getDefaultValue() { - return defaultValue; - } - - public ContinousHashProvider getHashProvider() { - return hashProvider; - } - - @Override - public V put(K key, V value) { - if (root != null) { - root = root.putValue(key, value, oldValueBox, hashProvider, defaultValue, hashProvider.getHash(key, 0), 0); - return oldValueBox.getOldValue(); - } else { - root = MutableNode.initialize(key, value, hashProvider, defaultValue); - return defaultValue; - } - } - - @Override - public void putAll(Cursor cursor) { - if (cursor.getDependingMaps().contains(this)) { - List keys = new LinkedList<>(); - List values = new LinkedList<>(); - while (cursor.move()) { - keys.add(cursor.getKey()); - values.add(cursor.getValue()); - } - Iterator keyIterator = keys.iterator(); - Iterator valueIterator = values.iterator(); - while (keyIterator.hasNext()) { - var key = keyIterator.next(); - var value = valueIterator.next(); - this.put(key,value); - } - } else { - while (cursor.move()) { - this.put(cursor.getKey(), cursor.getValue()); - } - } - } - - @Override - public V get(K key) { - if (root != null) { - return root.getValue(key, hashProvider, defaultValue, hashProvider.getHash(key, 0), 0); - } else { - return defaultValue; - } - } - - @Override - public long getSize() { - if (root == null) { - return 0; - } else { - return root.getSize(); - } - } - - @Override - public Cursor getAll() { - return new MapCursor<>(this.root, this); - } - - @Override - public DiffCursor getDiffCursor(long toVersion) { - InOrderMapCursor fromCursor = new InOrderMapCursor<>(this); - VersionedMapImpl toMap = (VersionedMapImpl) this.store.createMap(toVersion); - InOrderMapCursor toCursor = new InOrderMapCursor<>(toMap); - return new MapDiffCursor<>(this.defaultValue, fromCursor, toCursor); - } - - - @Override - public long commit() { - return this.store.commit(root, this); - } - - public void setRoot(Node root) { - this.root = root; - } - - @Override - public void restore(long state) { - root = this.store.revert(state); - } - - public String prettyPrint() { - if (this.root != null) { - StringBuilder s = new StringBuilder(); - this.root.prettyPrint(s, 0, -1); - return s.toString(); - } else { - return "empty tree"; - } - } - - @Override - public void checkIntegrity() { - if (this.root != null) { - this.root.checkIntegrity(hashProvider, defaultValue, 0); - } - } - - @Override - public int contentHashCode(ContentHashCode mode) { - // Calculating the root hashCode is always fast, because {@link Node} caches its hashCode. - if(root == null) { - return 0; - } else { - return root.hashCode(); - } - } - - @Override - public boolean contentEquals(AnyVersionedMap other) { - return other instanceof VersionedMapImpl otherImpl && Objects.equals(root, otherImpl.root); - } -} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java index cf117d95..47470236 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java @@ -5,9 +5,11 @@ */ package tools.refinery.store.map.internal; -import tools.refinery.store.map.ContinousHashProvider; +import tools.refinery.store.map.ContinuousHashProvider; import tools.refinery.store.map.VersionedMapStoreFactory; import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; +import tools.refinery.store.map.internal.delta.DeltaBasedVersionedMapStoreFactory; +import tools.refinery.store.map.internal.state.StateBasedVersionedMapStoreFactory; public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapStoreFactoryBuilder { @@ -16,14 +18,14 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt private StoreStrategy strategy = null; private Boolean transformToImmutable = null; private SharingStrategy sharingStrategy = null; - private ContinousHashProvider continousHashProvider = null; + private ContinuousHashProvider continuousHashProvider = null; private DeltaTransactionStrategy deltaTransactionStrategy = null; private StoreStrategy checkStrategy() { StoreStrategy currentStrategy = strategy; currentStrategy = mergeStrategies(currentStrategy, transformToImmutable, StoreStrategy.STATE); currentStrategy = mergeStrategies(currentStrategy, sharingStrategy, StoreStrategy.STATE); - currentStrategy = mergeStrategies(currentStrategy, continousHashProvider, StoreStrategy.STATE); + currentStrategy = mergeStrategies(currentStrategy, continuousHashProvider, StoreStrategy.STATE); currentStrategy = mergeStrategies(currentStrategy, deltaTransactionStrategy, StoreStrategy.DELTA); return currentStrategy; } @@ -77,8 +79,8 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt } @Override - public VersionedMapStoreFactoryBuilder stateBasedHashProvider(ContinousHashProvider hashProvider) { - this.continousHashProvider = hashProvider; + public VersionedMapStoreFactoryBuilder stateBasedHashProvider(ContinuousHashProvider hashProvider) { + this.continuousHashProvider = hashProvider; checkStrategy(); return this; } @@ -110,13 +112,13 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt } return switch (strategyToUse) { case STATE -> { - if(continousHashProvider == null) { + if(continuousHashProvider == null) { throw new IllegalArgumentException("Continuous hash provider is missing!"); } yield new StateBasedVersionedMapStoreFactory<>(defaultValue, getOrDefault(transformToImmutable,true), getOrDefault(sharingStrategy, SharingStrategy.SHARED_NODE_CACHE_IN_GROUP), - continousHashProvider); + continuousHashProvider); } case DELTA -> new DeltaBasedVersionedMapStoreFactory<>(defaultValue, getOrDefault(deltaTransactionStrategy, DeltaTransactionStrategy.LIST)); @@ -130,7 +132,7 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt ", strategy=" + strategy + ", stateBasedImmutableWhenCommitting=" + transformToImmutable + ", stateBasedNodeSharingStrategy=" + sharingStrategy + - ", hashProvider=" + continousHashProvider + + ", hashProvider=" + continuousHashProvider + ", deltaStorageStrategy=" + deltaTransactionStrategy + '}'; } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/DeltaBasedVersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/DeltaBasedVersionedMapStoreFactory.java new file mode 100644 index 00000000..cedcdc0b --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/DeltaBasedVersionedMapStoreFactory.java @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.delta; + +import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.map.VersionedMapStoreFactory; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; + +import java.util.ArrayList; +import java.util.List; + +public class DeltaBasedVersionedMapStoreFactory implements VersionedMapStoreFactory { + private final V defaultValue; + private final boolean summarizeChanges; + + public DeltaBasedVersionedMapStoreFactory(V defaultValue, + VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy deltaTransactionStrategy) { + this.defaultValue = defaultValue; + this.summarizeChanges = deltaTransactionStrategy == VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy.SET; + } + + @Override + public VersionedMapStore createOne() { + return new VersionedMapStoreDeltaImpl<>(summarizeChanges, defaultValue); + } + + @Override + public List> createGroup(int amount) { + List> result = new ArrayList<>(amount); + for(int i=0; i(summarizeChanges,defaultValue)); + } + return result; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/DeltaDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/DeltaDiffCursor.java new file mode 100644 index 00000000..ce10b246 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/DeltaDiffCursor.java @@ -0,0 +1,147 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.delta; + +import tools.refinery.store.map.AnyVersionedMap; +import tools.refinery.store.map.DiffCursor; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +public class DeltaDiffCursor implements DiffCursor { + final List[]> backwardTransactions; + final List[]> forwardTransactions; + + boolean started; + /** + * Denotes the direction of traversal. False means backwards, true means + * forward. + */ + boolean direction; + int listIndex; + int arrayIndex; + + public DeltaDiffCursor(List[]> backwardTransactions, List[]> forwardTransactions) { + this.backwardTransactions = backwardTransactions; + this.forwardTransactions = forwardTransactions; + + if (!backwardTransactions.isEmpty()) { + direction = false; + listIndex = 0; + arrayIndex = backwardTransactions.get(listIndex).length - 1; + } else if (!forwardTransactions.isEmpty()) { + direction = true; + listIndex = forwardTransactions.size() - 1; + arrayIndex = 0; + } else { + direction = true; + listIndex = -1; + } + started = false; + } + + protected MapDelta getCurrentDelta() { + final List[]> list; + if (!direction) { + list = this.backwardTransactions; + } else { + list = this.forwardTransactions; + } + return list.get(listIndex)[arrayIndex]; + } + + @Override + public K getKey() { + return getCurrentDelta().getKey(); + } + + @Override + public V getValue() { + return getToValue(); + } + + @Override + public boolean isTerminated() { + return this.direction && listIndex == -1; + } + + + @Override + public boolean move() { + if(!started) { + started = true; + return !isTerminated(); + } else if (isTerminated()) { + return false; + } else { + if (this.direction) { + if (arrayIndex+1 < forwardTransactions.get(listIndex).length) { + arrayIndex++; + return true; + } else { + if (listIndex-1 >= 0) { + listIndex--; + arrayIndex = 0; + return true; + } else { + listIndex = -1; + return false; + } + } + } else { + if (arrayIndex > 0) { + arrayIndex--; + return true; + } else { + if (listIndex+1 < backwardTransactions.size()) { + listIndex++; + this.arrayIndex = backwardTransactions.get(listIndex).length - 1; + return true; + } else { + this.direction = true; + if (!this.forwardTransactions.isEmpty()) { + listIndex = forwardTransactions.size() - 1; + arrayIndex = 0; + return true; + } else { + listIndex = -1; + return false; + } + } + } + } + } + } + + @Override + public boolean isDirty() { + return false; + } + + @Override + public Set getDependingMaps() { + return Collections.emptySet(); + } + + @Override + public V getFromValue() { + if(this.direction) { + return getCurrentDelta().getOldValue(); + } else { + return getCurrentDelta().getNewValue(); + } + } + + @Override + public V getToValue() { + if(this.direction) { + return getCurrentDelta().getNewValue(); + } else { + return getCurrentDelta().getOldValue(); + } + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapDelta.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapDelta.java new file mode 100644 index 00000000..0c0cc906 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapDelta.java @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.delta; + +public record MapDelta(K key, V oldValue, V newValue) { + public K getKey() { + return key; + } + + public V getOldValue() { + return oldValue; + } + + public V getNewValue() { + return newValue; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java new file mode 100644 index 00000000..7f9ccd7f --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.delta; + +import java.util.Arrays; +import java.util.Objects; + +public record MapTransaction(MapDelta[] deltas, long version, MapTransaction parent) { + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(deltas); + result = prime * result + Objects.hash(parent, version); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + @SuppressWarnings("unchecked") + MapTransaction other = (MapTransaction) obj; + return Arrays.equals(deltas, other.deltas) && Objects.equals(parent, other.parent) && version == other.version; + } + + @Override + public String toString() { + return "MapTransaction " + version + " " + Arrays.toString(deltas); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaArrayStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaArrayStore.java new file mode 100644 index 00000000..1f6a9000 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaArrayStore.java @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.delta; + +import java.util.ArrayList; +import java.util.List; + +public class UncommittedDeltaArrayStore implements UncommittedDeltaStore { + final List> uncommittedOldValues = new ArrayList<>(); + + @Override + public void processChange(K key, V oldValue, V newValue) { + uncommittedOldValues.add(new MapDelta<>(key, oldValue, newValue)); + } + + @Override + public MapDelta[] extractDeltas() { + if (uncommittedOldValues.isEmpty()) { + return null; + } else { + @SuppressWarnings("unchecked") + MapDelta[] result = uncommittedOldValues.toArray(new MapDelta[0]); + return result; + } + } + + @Override + public MapDelta[] extractAndDeleteDeltas() { + MapDelta[] res = extractDeltas(); + this.uncommittedOldValues.clear(); + return res; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaMapStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaMapStore.java new file mode 100644 index 00000000..644884a6 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaMapStore.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.delta; + +import java.util.*; +import java.util.Map.Entry; + +import tools.refinery.store.map.VersionedMap; + +public class UncommittedDeltaMapStore implements UncommittedDeltaStore { + final VersionedMap source; + final Map uncommittedOldValues = new HashMap<>(); + + public UncommittedDeltaMapStore(VersionedMap source) { + this.source = source; + } + + @Override + public void processChange(K key, V oldValue, V newValue) { + if(!uncommittedOldValues.containsKey(key)) { + this.uncommittedOldValues.put(key,oldValue); + } + } + + @Override + public MapDelta[] extractDeltas() { + if (uncommittedOldValues.isEmpty()) { + return null; + } else { + @SuppressWarnings("unchecked") + MapDelta[] deltas = new MapDelta[uncommittedOldValues.size()]; + int i = 0; + for (Entry entry : uncommittedOldValues.entrySet()) { + final K key = entry.getKey(); + final V oldValue = entry.getValue(); + final V newValue = source.get(key); + deltas[i++] = new MapDelta<>(key, oldValue, newValue); + } + + return deltas; + } + } + + @Override + public MapDelta[] extractAndDeleteDeltas() { + MapDelta[] res = extractDeltas(); + this.uncommittedOldValues.clear(); + return res; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaStore.java new file mode 100644 index 00000000..ecd33c5f --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/UncommittedDeltaStore.java @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.delta; + +public interface UncommittedDeltaStore { + void processChange(K key, V oldValue, V newValue); + + MapDelta[] extractDeltas(); + + MapDelta[] extractAndDeleteDeltas(); + + default void checkIntegrity() { + MapDelta[] extractedDeltas = extractDeltas(); + if(extractedDeltas != null) { + for(var uncommittedOldValue : extractedDeltas) { + if(uncommittedOldValue == null) { + throw new IllegalArgumentException("Null entry in deltas!"); + } + if(uncommittedOldValue.getKey() == null) { + throw new IllegalStateException("Null key in deltas!"); + } + } + } + } + +} 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 new file mode 100644 index 00000000..5bb864ac --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapDeltaImpl.java @@ -0,0 +1,220 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.delta; + +import java.util.*; + +import tools.refinery.store.map.*; +import tools.refinery.store.map.IteratorAsCursor; + +public class VersionedMapDeltaImpl implements VersionedMap { + protected final VersionedMapStoreDeltaImpl store; + + final Map current; + + final UncommittedDeltaStore uncommittedStore; + MapTransaction previous; + + protected final V defaultValue; + + public VersionedMapDeltaImpl(VersionedMapStoreDeltaImpl store, boolean summarizeChanges, V defaultValue) { + this.store = store; + this.defaultValue = defaultValue; + + current = new HashMap<>(); + if (summarizeChanges) { + this.uncommittedStore = new UncommittedDeltaMapStore<>(this); + } else { + this.uncommittedStore = new UncommittedDeltaArrayStore<>(); + } + } + + @Override + public V getDefaultValue() { + return defaultValue; + } + + @Override + public long commit() { + MapDelta[] deltas = uncommittedStore.extractAndDeleteDeltas(); + long[] versionContainer = new long[1]; + this.previous = this.store.appendTransaction(deltas, previous, versionContainer); + return versionContainer[0]; + } + + @Override + public void restore(long state) { + // 1. restore uncommitted states + MapDelta[] uncommitted = this.uncommittedStore.extractAndDeleteDeltas(); + if (uncommitted != null) { + backward(uncommitted); + } + + // 2. get common ancestor + final MapTransaction parent; + List[]> forward = new ArrayList<>(); + if (this.previous == null) { + parent = this.store.getPath(state, forward); + this.forward(forward); + } else { + List[]> backward = new ArrayList<>(); + parent = this.store.getPath(this.previous.version(), state, backward, forward); + this.backward(backward); + this.forward(forward); + } + this.previous = parent; + } + + protected void forward(List[]> changes) { + for (int i = changes.size() - 1; i >= 0; i--) { + forward(changes.get(i)); + } + } + + protected void backward(List[]> changes) { + for (int i = 0; i < changes.size(); i++) { + backward(changes.get(i)); + } + } + + protected void forward(MapDelta[] changes) { + for (int i = 0; i < changes.length; i++) { + final MapDelta change = changes[i]; + K key = change.getKey(); + V newValue = change.getNewValue(); + + if(newValue == defaultValue) { + current.remove(key); + } else { + current.put(key,newValue); + } + } + } + + protected void backward(MapDelta[] changes) { + for (int i = changes.length - 1; i >= 0; i--) { + final MapDelta change = changes[i]; + K key = change.getKey(); + V oldValue = change.oldValue(); + + if(oldValue == defaultValue) { + current.remove(key); + } else { + current.put(key,oldValue); + } + } + } + + @Override + public V get(K key) { + return current.getOrDefault(key, defaultValue); + } + + @Override + public Cursor getAll() { + return new IteratorAsCursor<>(this, current); + } + + @Override + public V put(K key, V value) { + final V oldValue; + if (Objects.equals(value, defaultValue)) { + final V res = current.remove(key); + if (res == null) { + // no changes: default > default + oldValue = defaultValue; + } else { + oldValue = res; + } + } else { + final var mapValue = current.put(key, value); + if (mapValue == null) { + oldValue = defaultValue; + } else { + oldValue = mapValue; + } + } + if(!Objects.equals(oldValue,value)) { + uncommittedStore.processChange(key, oldValue, value); + } + return oldValue; + } + + @Override + public void putAll(Cursor cursor) { + if (cursor.getDependingMaps().contains(this)) { + List keys = new ArrayList<>(); + List values = new ArrayList<>(); + while (cursor.move()) { + keys.add(cursor.getKey()); + values.add(cursor.getValue()); + } + for (int i = 0; i < keys.size(); i++) { + this.put(keys.get(i), values.get(i)); + } + } else { + while (cursor.move()) { + this.put(cursor.getKey(), cursor.getValue()); + } + } + } + + @Override + public long getSize() { + return current.size(); + } + + @Override + public DiffCursor getDiffCursor(long state) { + MapDelta[] backward = this.uncommittedStore.extractDeltas(); + List[]> backwardTransactions = new ArrayList<>(); + List[]> forwardTransactions = new ArrayList<>(); + + if (backward != null) { + backwardTransactions.add(backward); + } + + if (this.previous != null) { + store.getPath(this.previous.version(), state, backwardTransactions, forwardTransactions); + } else { + store.getPath(state, forwardTransactions); + } + + return new DeltaDiffCursor<>(backwardTransactions, forwardTransactions); + } + + @Override + public int contentHashCode(ContentHashCode mode) { + return this.current.hashCode(); + } + + @Override + public boolean contentEquals(AnyVersionedMap other) { + if (other instanceof VersionedMapDeltaImpl versioned) { + if (versioned == this) { + return true; + } else { + return Objects.equals(this.defaultValue, versioned.defaultValue) && Objects.equals(this.current, versioned.current); + } + } else { + throw new UnsupportedOperationException("Comparing different map implementations is ineffective."); + } + } + + @Override + public void checkIntegrity() { + this.uncommittedStore.checkIntegrity(); + + for (var entry : this.current.entrySet()) { + var value = entry.getValue(); + if (value == this.defaultValue) { + throw new IllegalStateException("Default value stored in map!"); + } else if (value == null) { + throw new IllegalStateException("null value stored in map!"); + } + } + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java new file mode 100644 index 00000000..7f56ea77 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java @@ -0,0 +1,101 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.delta; + +import tools.refinery.store.map.DiffCursor; +import tools.refinery.store.map.VersionedMap; +import tools.refinery.store.map.VersionedMapStore; + +import java.util.*; + +public class VersionedMapStoreDeltaImpl implements VersionedMapStore { + // Configuration + protected final boolean summarizeChanges; + + // Static data + protected final V defaultValue; + + // Dynamic data + protected final Map> states = new HashMap<>(); + protected long nextID = 0; + + public VersionedMapStoreDeltaImpl(boolean summarizeChanges, V defaultValue) { + this.summarizeChanges = summarizeChanges; + this.defaultValue = defaultValue; + } + + @Override + public VersionedMap createMap() { + return new VersionedMapDeltaImpl<>(this, this.summarizeChanges, this.defaultValue); + } + + @Override + public VersionedMap createMap(long state) { + VersionedMapDeltaImpl result = new VersionedMapDeltaImpl<>(this, this.summarizeChanges, this.defaultValue); + result.restore(state); + return result; + } + + public synchronized MapTransaction appendTransaction(MapDelta[] deltas, MapTransaction previous, long[] versionContainer) { + long version = nextID++; + versionContainer[0] = version; + if (deltas == null) { + states.put(version, previous); + return previous; + } else { + MapTransaction transaction = new MapTransaction<>(deltas, version, previous); + states.put(version, transaction); + return transaction; + } + } + + private synchronized MapTransaction getState(long state) { + return states.get(state); + } + + public MapTransaction getPath(long to, List[]> forwardTransactions) { + final MapTransaction target = getState(to); + MapTransaction toTransaction = target; + while (toTransaction != null) { + forwardTransactions.add(toTransaction.deltas()); + toTransaction = toTransaction.parent(); + } + return target; + } + + public MapTransaction getPath(long from, long to, + List[]> backwardTransactions, + List[]> forwardTransactions) { + MapTransaction fromTransaction = getState(from); + final MapTransaction target = getState(to); + MapTransaction toTransaction = target; + + while (fromTransaction != toTransaction) { + if (fromTransaction == null || (toTransaction != null && fromTransaction.version() < toTransaction.version())) { + forwardTransactions.add(toTransaction.deltas()); + toTransaction = toTransaction.parent(); + } else { + backwardTransactions.add(fromTransaction.deltas()); + fromTransaction = fromTransaction.parent(); + } + } + return target; + } + + + @Override + public synchronized Set getStates() { + return new HashSet<>(states.keySet()); + } + + @Override + public DiffCursor getDiffCursor(long fromState, long toState) { + List[]> backwardTransactions = new ArrayList<>(); + List[]> forwardTransactions = new ArrayList<>(); + getPath(fromState, toState, backwardTransactions, forwardTransactions); + return new DeltaDiffCursor<>(backwardTransactions, forwardTransactions); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java new file mode 100644 index 00000000..722f9ed7 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java @@ -0,0 +1,413 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +import java.util.Arrays; +import java.util.Map; + +import tools.refinery.store.map.ContinuousHashProvider; + +public class ImmutableNode extends Node { + /** + * Bitmap defining the stored key and values. + */ + final int dataMap; + /** + * Bitmap defining the positions of further nodes. + */ + final int nodeMap; + /** + * Stores Keys, Values, and sub-nodes. Structure: (K,V)*,NODE; NODES are stored + * backwards. + */ + final Object[] content; + + /** + * Hash code derived from immutable hash code + */ + final int precalculatedHash; + + private ImmutableNode(int dataMap, int nodeMap, Object[] content, int precalculatedHash) { + super(); + this.dataMap = dataMap; + this.nodeMap = nodeMap; + this.content = content; + this.precalculatedHash = precalculatedHash; + } + + /** + * Constructor that copies a mutable node to an immutable. + * + * @param node A mutable node. + * @param cache A cache of existing immutable nodes. It can be used to search + * and place reference immutable nodes. It can be null, if no cache + * available. + * @return an immutable version of the input node. + */ + static ImmutableNode constructImmutable(MutableNode node, Map, ImmutableNode> cache) { + // 1. try to return from cache + if (cache != null) { + ImmutableNode cachedResult = cache.get(node); + if (cachedResult != null) { + // 1.1 Already cached, return from cache. + return cachedResult; + } + } + + // 2. otherwise construct a new ImmutableNode + int size = 0; + for (int i = 0; i < node.content.length; i++) { + if (node.content[i] != null) { + size++; + } + } + + int datas = 0; + int nodes = 0; + int resultDataMap = 0; + int resultNodeMap = 0; + final Object[] resultContent = new Object[size]; + int bitPosition = 1; + for (int i = 0; i < FACTOR; i++) { + Object key = node.content[i * 2]; + if (key != null) { + resultDataMap |= bitPosition; + resultContent[datas * 2] = key; + resultContent[datas * 2 + 1] = node.content[i * 2 + 1]; + datas++; + } else { + @SuppressWarnings("unchecked") var subnode = (Node) node.content[i * 2 + 1]; + if (subnode != null) { + ImmutableNode immutableSubNode = subnode.toImmutable(cache); + resultNodeMap |= bitPosition; + resultContent[size - 1 - nodes] = immutableSubNode; + nodes++; + } + } + bitPosition <<= 1; + } + final int resultHash = node.hashCode(); + var newImmutable = new ImmutableNode(resultDataMap, resultNodeMap, resultContent, resultHash); + + // 3. save new immutable. + if (cache != null) { + cache.put(newImmutable, newImmutable); + } + return newImmutable; + } + + private int index(int bitmap, int bitpos) { + return Integer.bitCount(bitmap & (bitpos - 1)); + } + + @Override + public V getValue(K key, ContinuousHashProvider hashProvider, V defaultValue, int hash, int depth) { + int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); + int bitposition = 1 << selectedHashFragment; + // If the key is stored as a data + if ((dataMap & bitposition) != 0) { + int keyIndex = 2 * index(dataMap, bitposition); + @SuppressWarnings("unchecked") K keyCandidate = (K) content[keyIndex]; + if (keyCandidate.equals(key)) { + @SuppressWarnings("unchecked") V value = (V) content[keyIndex + 1]; + return value; + } else { + return defaultValue; + } + } + // the key is stored as a node + else if ((nodeMap & bitposition) != 0) { + int keyIndex = content.length - 1 - index(nodeMap, bitposition); + @SuppressWarnings("unchecked") var subNode = (ImmutableNode) content[keyIndex]; + int newDepth = incrementDepth(depth); + int newHash = newHash(hashProvider, key, hash, newDepth); + return subNode.getValue(key, hashProvider, defaultValue, newHash, newDepth); + } + // the key is not stored at all + else { + return defaultValue; + } + } + + @Override + public Node putValue(K key, V value, OldValueBox oldValue, ContinuousHashProvider hashProvider, V defaultValue, int hash, int depth) { + int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); + int bitPosition = 1 << selectedHashFragment; + if ((dataMap & bitPosition) != 0) { + int keyIndex = 2 * index(dataMap, bitPosition); + @SuppressWarnings("unchecked") K keyCandidate = (K) content[keyIndex]; + if (keyCandidate.equals(key)) { + if (value == defaultValue) { + // delete + MutableNode mutable = this.toMutable(); + return mutable.removeEntry(selectedHashFragment, oldValue); + } else if (value == content[keyIndex + 1]) { + // don't change + oldValue.setOldValue(value); + return this; + } else { + // update existing value + MutableNode mutable = this.toMutable(); + return mutable.updateValue(value, oldValue, selectedHashFragment); + } + } else { + if (value == defaultValue) { + // don't change + oldValue.setOldValue(defaultValue); + return this; + } else { + // add new key + value + MutableNode mutable = this.toMutable(); + return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); + } + } + } else if ((nodeMap & bitPosition) != 0) { + int keyIndex = content.length - 1 - index(nodeMap, bitPosition); + @SuppressWarnings("unchecked") var subNode = (ImmutableNode) content[keyIndex]; + int newDepth = incrementDepth(depth); + int newHash = newHash(hashProvider, key, hash, newDepth); + var newsubNode = subNode.putValue(key, value, oldValue, hashProvider, defaultValue, newHash, newDepth); + + if (subNode == newsubNode) { + // nothing changed + return this; + } else { + MutableNode mutable = toMutable(); + return mutable.updateWithSubNode(selectedHashFragment, newsubNode, + (value == null && defaultValue == null) || (value != null && value.equals(defaultValue))); + } + } else { + // add new key + value + MutableNode mutable = this.toMutable(); + return mutable.putValue(key, value, oldValue, hashProvider, defaultValue, hash, depth); + } + } + + @Override + public long getSize() { + int result = Integer.bitCount(this.dataMap); + for (int subnodeIndex = 0; subnodeIndex < Integer.bitCount(this.nodeMap); subnodeIndex++) { + @SuppressWarnings("unchecked") var subnode = (ImmutableNode) this.content[this.content.length - 1 - subnodeIndex]; + result += subnode.getSize(); + } + return result; + } + + @Override + protected MutableNode toMutable() { + return new MutableNode<>(this); + } + + @Override + public ImmutableNode toImmutable(Map, ImmutableNode> cache) { + return this; + } + + @Override + protected MutableNode isMutable() { + return null; + } + + @SuppressWarnings("unchecked") + @Override + boolean moveToNext(MapCursor cursor) { + // 1. try to move to data + int datas = Integer.bitCount(this.dataMap); + if (cursor.dataIndex != MapCursor.INDEX_FINISH) { + int newDataIndex = cursor.dataIndex + 1; + if (newDataIndex < datas) { + cursor.dataIndex = newDataIndex; + cursor.key = (K) this.content[newDataIndex * 2]; + cursor.value = (V) this.content[newDataIndex * 2 + 1]; + return true; + } else { + cursor.dataIndex = MapCursor.INDEX_FINISH; + } + } + + // 2. look inside the subnodes + int nodes = Integer.bitCount(this.nodeMap); + if(cursor.nodeIndexStack.peek()==null) { + throw new IllegalStateException("Cursor moved to the next state when the state is empty."); + } + int newNodeIndex = cursor.nodeIndexStack.peek() + 1; + if (newNodeIndex < nodes) { + // 2.1 found next subnode, move down to the subnode + Node subnode = (Node) this.content[this.content.length - 1 - newNodeIndex]; + cursor.dataIndex = MapCursor.INDEX_START; + cursor.nodeIndexStack.pop(); + cursor.nodeIndexStack.push(newNodeIndex); + cursor.nodeIndexStack.push(MapCursor.INDEX_START); + cursor.nodeStack.push(subnode); + return subnode.moveToNext(cursor); + } else { + // 3. no subnode found, move up + cursor.nodeStack.pop(); + cursor.nodeIndexStack.pop(); + if (!cursor.nodeStack.isEmpty()) { + Node supernode = cursor.nodeStack.peek(); + return supernode.moveToNext(cursor); + } else { + cursor.key = null; + cursor.value = null; + return false; + } + } + } + + @Override + @SuppressWarnings("unchecked") + boolean moveToNextInorder(InOrderMapCursor cursor) { + if(cursor.nodeIndexStack.peek()==null) { + throw new IllegalStateException("Cursor moved to the next state when the state is empty."); + } + + int position = cursor.nodeIndexStack.peek(); + for (int index = position + 1; index < FACTOR; index++) { + final int mask = 1< subnode = (Node) this.content[this.content.length - 1 - index(nodeMap, mask)]; + cursor.nodeIndexStack.pop(); + cursor.nodeIndexStack.push(index); + cursor.nodeIndexStack.push(InOrderMapCursor.INDEX_START); + cursor.nodeStack.push(subnode); + + return subnode.moveToNextInorder(cursor); + } + } + + // nothing found + cursor.nodeStack.pop(); + cursor.nodeIndexStack.pop(); + if (!cursor.nodeStack.isEmpty()) { + Node supernode = cursor.nodeStack.peek(); + return supernode.moveToNextInorder(cursor); + } else { + cursor.key = null; + cursor.value = null; + return false; + } + } + + @Override + public void prettyPrint(StringBuilder builder, int depth, int code) { + builder.append("\t".repeat(Math.max(0, depth))); + if (code >= 0) { + builder.append(code); + builder.append(":"); + } + builder.append("Immutable("); + boolean hadContent = false; + int dataMask = 1; + for (int i = 0; i < FACTOR; i++) { + if ((dataMask & dataMap) != 0) { + if (hadContent) { + builder.append(","); + } + builder.append(i); + builder.append(":["); + builder.append(content[2 * index(dataMap, dataMask)].toString()); + builder.append("]->["); + builder.append(content[2 * index(dataMap, dataMask) + 1].toString()); + builder.append("]"); + hadContent = true; + } + dataMask <<= 1; + } + builder.append(")"); + int nodeMask = 1; + for (int i = 0; i < FACTOR; i++) { + if ((nodeMask & nodeMap) != 0) { + @SuppressWarnings("unchecked") Node subNode = (Node) content[content.length - 1 - index(nodeMap, nodeMask)]; + builder.append("\n"); + subNode.prettyPrint(builder, incrementDepth(depth), i); + } + nodeMask <<= 1; + } + } + + @Override + public void checkIntegrity(ContinuousHashProvider hashProvider, V defaultValue, int depth) { + if (depth > 0) { + boolean orphaned = Integer.bitCount(dataMap) == 1 && nodeMap == 0; + if (orphaned) { + throw new IllegalStateException("Orphaned node! " + dataMap + ": " + content[0]); + } + } + // check the place of data + + // check subnodes + for (int i = 0; i < Integer.bitCount(nodeMap); i++) { + @SuppressWarnings("unchecked") var subnode = (Node) this.content[this.content.length - 1 - i]; + if (!(subnode instanceof ImmutableNode)) { + throw new IllegalStateException("Immutable node contains mutable subnodes!"); + } else { + subnode.checkIntegrity(hashProvider, defaultValue, incrementDepth(depth)); + } + } + } + + @Override + public int hashCode() { + return this.precalculatedHash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (obj instanceof ImmutableNode other) { + return precalculatedHash == other.precalculatedHash && dataMap == other.dataMap && nodeMap == other.nodeMap && Arrays.deepEquals(content, other.content); + } else if (obj instanceof MutableNode mutableObj) { + return ImmutableNode.compareImmutableMutable(this, mutableObj); + } else { + return false; + } + } + + public static boolean compareImmutableMutable(ImmutableNode immutable, MutableNode mutable) { + int datas = 0; + int nodes = 0; + final int immutableLength = immutable.content.length; + for (int i = 0; i < FACTOR; i++) { + Object key = mutable.content[i * 2]; + // For each key candidate + if (key != null) { + // Check whether a new Key-Value pair can fit into the immutable container + if (datas * 2 + nodes + 2 <= immutableLength) { + if (!immutable.content[datas * 2].equals(key) || !immutable.content[datas * 2 + 1].equals(mutable.content[i * 2 + 1])) { + return false; + } + } else return false; + datas++; + } else { + var mutableSubnode = (Node) mutable.content[i * 2 + 1]; + if (mutableSubnode != null) { + if (datas * 2 + nodes + 1 <= immutableLength) { + Object immutableSubNode = immutable.content[immutableLength - 1 - nodes]; + if (!mutableSubnode.equals(immutableSubNode)) { + return false; + } + nodes++; + } else { + return false; + } + } + } + } + + return datas * 2 + nodes == immutable.content.length; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/InOrderMapCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/InOrderMapCursor.java new file mode 100644 index 00000000..7cc91004 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/InOrderMapCursor.java @@ -0,0 +1,146 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +import tools.refinery.store.map.AnyVersionedMap; +import tools.refinery.store.map.ContentHashCode; +import tools.refinery.store.map.Cursor; +import tools.refinery.store.map.VersionedMap; + +import java.util.*; + +public class InOrderMapCursor implements Cursor { + // Constants + static final int INDEX_START = -1; + + // Tree stack + ArrayDeque> nodeStack; + ArrayDeque nodeIndexStack; + + + // Values + K key; + V value; + + // Hash code for checking concurrent modifications + final VersionedMap map; + final int creationHash; + + public InOrderMapCursor(VersionedMapStateImpl map) { + // Initializing tree stack + super(); + this.nodeStack = new ArrayDeque<>(); + this.nodeIndexStack = new ArrayDeque<>(); + if (map.root != null) { + this.nodeStack.add(map.root); + this.nodeIndexStack.push(INDEX_START); + } + + // Initializing cache + this.key = null; + this.value = null; + + // Initializing state + this.map = map; + this.creationHash = map.contentHashCode(ContentHashCode.APPROXIMATE_FAST); + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public boolean isTerminated() { + return this.nodeStack.isEmpty(); + } + + public boolean move() { + if (isDirty()) { + throw new ConcurrentModificationException(); + } + if (!isTerminated()) { + var node = this.nodeStack.peek(); + if (node == null) { + throw new IllegalStateException("Cursor is not terminated but the current node is missing"); + } + boolean result = node.moveToNextInorder(this); + if (this.nodeIndexStack.size() != this.nodeStack.size()) { + throw new IllegalArgumentException("Node stack is corrupted by illegal moves!"); + } + return result; + } + return false; + } + + public boolean skipCurrentNode() { + nodeStack.pop(); + nodeIndexStack.pop(); + return move(); + } + + @Override + public boolean isDirty() { + return this.map.contentHashCode(ContentHashCode.APPROXIMATE_FAST) != this.creationHash; + } + + @Override + public Set getDependingMaps() { + return Set.of(this.map); + } + + public static boolean sameSubNode(InOrderMapCursor cursor1, InOrderMapCursor cursor2) { + Node nodeOfCursor1 = cursor1.nodeStack.peek(); + Node nodeOfCursor2 = cursor2.nodeStack.peek(); + return Objects.equals(nodeOfCursor1, nodeOfCursor2); + } + + /** + * Compares the state of two cursors started on two {@link VersionedMap} of the same + * {@link tools.refinery.store.map.VersionedMapStore}. + * @param Key type + * @param Value type + * @param cursor1 first cursor + * @param cursor2 second cursor + * @return Positive number if cursor 1 is behind, negative number if cursor 2 is behind, and 0 if they are at the + * same position. + */ + public static int comparePosition(InOrderMapCursor cursor1, InOrderMapCursor cursor2) { + // If the state does not determine the order, then compare @nodeIndexStack. + Iterator nodeIndexStack1 = cursor1.nodeIndexStack.descendingIterator(); + Iterator nodeIndexStack2 = cursor2.nodeIndexStack.descendingIterator(); + + while(nodeIndexStack1.hasNext() && nodeIndexStack2.hasNext()){ + final int index1 = nodeIndexStack1.next(); + final int index2 = nodeIndexStack2.next(); + if(index1 < index2) { + return 1; + } else if(index1 > index2) { + return -1; + } + } + + return 0; + } + + /** + * Compares the depth of two cursors started on @{@link VersionedMap} of the same + * {@link tools.refinery.store.map.VersionedMapStore}. + * @param Key type + * @param Value type + * @param cursor1 first cursor + * @param cursor2 second cursor + * @return Positive number if cursor 1 is deeper, negative number if cursor 2 is deeper, and 0 if they are at the + * same depth. + */ + public static int compareDepth(InOrderMapCursor cursor1, InOrderMapCursor cursor2) { + int d1 = cursor1.nodeIndexStack.size(); + int d2 = cursor2.nodeIndexStack.size(); + return Integer.compare(d1, d2); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MapCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MapCursor.java new file mode 100644 index 00000000..0db50d04 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MapCursor.java @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +import tools.refinery.store.map.AnyVersionedMap; +import tools.refinery.store.map.ContentHashCode; +import tools.refinery.store.map.Cursor; +import tools.refinery.store.map.VersionedMap; + +import java.util.ArrayDeque; +import java.util.ConcurrentModificationException; +import java.util.Set; + +public class MapCursor implements Cursor { + // Constants + static final int INDEX_START = -1; + static final int INDEX_FINISH = -2; + + // Tree stack + ArrayDeque> nodeStack; + ArrayDeque nodeIndexStack; + int dataIndex; + + // Values + K key; + V value; + + // Hash code for checking concurrent modifications + final VersionedMap map; + final int creationHash; + + public MapCursor(Node root, VersionedMap map) { + // Initializing tree stack + super(); + this.nodeStack = new ArrayDeque<>(); + this.nodeIndexStack = new ArrayDeque<>(); + if (root != null) { + this.nodeStack.add(root); + this.nodeIndexStack.push(INDEX_START); + } + + this.dataIndex = INDEX_START; + + // Initializing cache + this.key = null; + this.value = null; + + // Initializing state + this.map = map; + this.creationHash = map.contentHashCode(ContentHashCode.APPROXIMATE_FAST); + } + + public K getKey() { + return key; + } + + public V getValue() { + return value; + } + + public boolean isTerminated() { + return this.nodeStack.isEmpty(); + } + + public boolean move() { + if (isDirty()) { + throw new ConcurrentModificationException(); + } + if (!isTerminated()) { + var node = this.nodeStack.peek(); + if (node == null) { + throw new IllegalStateException("Cursor is not terminated but the current node is missing"); + } + boolean result = node.moveToNext(this); + if (this.nodeIndexStack.size() != this.nodeStack.size()) { + throw new IllegalArgumentException("Node stack is corrupted by illegal moves!"); + } + return result; + } + return false; + } + + @Override + public boolean isDirty() { + return this.map.contentHashCode(ContentHashCode.APPROXIMATE_FAST) != this.creationHash; + } + + @Override + public Set getDependingMaps() { + return Set.of(this.map); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MapDiffCursor.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MapDiffCursor.java new file mode 100644 index 00000000..083bf8cf --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MapDiffCursor.java @@ -0,0 +1,264 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +import tools.refinery.store.map.AnyVersionedMap; +import tools.refinery.store.map.Cursor; +import tools.refinery.store.map.DiffCursor; + +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * A cursor representing the difference between two states of a map. + * + * @author Oszkar Semerath + */ +public class MapDiffCursor implements DiffCursor, Cursor { + private enum State { + /** + * initialized state. + */ + INIT, + /** + * Unstable state. + */ + MOVING_MOVING_SAME_KEY_SAME_VALUE, + /** + * Both cursors are moving, and they are on the same sub-node. + */ + MOVING_MOVING_SAME_NODE, + /** + * Both cursors are moving, cursor 1 is behind. + */ + MOVING_MOVING_BEHIND1, + /** + * Both cursors are moving, cursor 2 is behind. + */ + MOVING_MOVING_BEHIND2, + /** + * Both cursors are moving, cursor 1 is on the same key as cursor 2, values are different + */ + MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE, + /** + * Cursor 1 is moving, Cursor 2 is terminated. + */ + MOVING_TERMINATED, + /** + * Cursor 1 is terminated , Cursor 2 is moving. + */ + TERMINATED_MOVING, + /** + * Both cursors are terminated. + */ + TERMINATED_TERMINATED, + /** + * Both Cursors are moving, and they are on an incomparable position. + * It is resolved by showing Cursor 1. + */ + MOVING_MOVING_HASH1, + /** + * Both Cursors are moving, and they are on an incomparable position. + * It is resolved by showing Cursor 2. + */ + MOVING_MOVING_HASH2 + } + + /** + * Default nodeId representing missing elements. + */ + private final V defaultValue; + private final InOrderMapCursor cursor1; + private final InOrderMapCursor cursor2; + + // State + State state = State.INIT; + + // Values + private K key; + private V fromValue; + private V toValue; + + + public MapDiffCursor(V defaultValue, InOrderMapCursor cursor1, InOrderMapCursor cursor2) { + super(); + this.defaultValue = defaultValue; + this.cursor1 = cursor1; + this.cursor2 = cursor2; + } + + @Override + public K getKey() { + return key; + } + + @Override + public V getFromValue() { + return fromValue; + } + + @Override + public V getToValue() { + return toValue; + } + + @Override + public V getValue() { + return getToValue(); + } + + public boolean isTerminated() { + return this.state == State.TERMINATED_TERMINATED; + } + + @Override + public boolean isDirty() { + return this.cursor1.isDirty() || this.cursor2.isDirty(); + } + + @Override + public Set getDependingMaps() { + return Stream.concat(cursor1.getDependingMaps().stream(), cursor2.getDependingMaps().stream()).map(AnyVersionedMap.class::cast).collect(Collectors.toUnmodifiableSet()); + } + + private boolean isInStableState() { + return this.state != State.MOVING_MOVING_SAME_KEY_SAME_VALUE + && this.state != State.MOVING_MOVING_SAME_NODE && this.state != State.INIT; + } + + private boolean updateAndReturnWithResult() { + return switch (this.state) { + case INIT -> throw new IllegalStateException("DiffCursor terminated without starting!"); + case MOVING_MOVING_SAME_KEY_SAME_VALUE, MOVING_MOVING_SAME_NODE -> + throw new IllegalStateException("DiffCursor terminated in unstable state!"); + case MOVING_MOVING_BEHIND1, MOVING_TERMINATED, MOVING_MOVING_HASH1 -> { + this.key = this.cursor1.getKey(); + this.fromValue = this.cursor1.getValue(); + this.toValue = this.defaultValue; + yield true; + } + case MOVING_MOVING_BEHIND2, TERMINATED_MOVING, MOVING_MOVING_HASH2 -> { + this.key = this.cursor2.getKey(); + this.fromValue = this.defaultValue; + this.toValue = cursor2.getValue(); + yield true; + } + case MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE -> { + this.key = this.cursor1.getKey(); + this.fromValue = this.cursor1.getValue(); + this.toValue = this.cursor2.getValue(); + yield true; + } + case TERMINATED_TERMINATED -> { + this.key = null; + this.fromValue = null; + this.toValue = null; + yield false; + } + }; + } + + public boolean move() { + do { + this.state = moveOne(this.state); + } while (!isInStableState()); + return updateAndReturnWithResult(); + } + + private State moveOne(State currentState) { + return switch (currentState) { + case INIT, MOVING_MOVING_HASH2, MOVING_MOVING_SAME_KEY_SAME_VALUE, MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE -> { + boolean cursor1Moved = cursor1.move(); + boolean cursor2Moved = cursor2.move(); + yield recalculateStateAfterCursorMovement(cursor1Moved, cursor2Moved); + } + case MOVING_MOVING_SAME_NODE -> { + boolean cursor1Moved = cursor1.skipCurrentNode(); + boolean cursor2Moved = cursor2.skipCurrentNode(); + yield recalculateStateAfterCursorMovement(cursor1Moved, cursor2Moved); + } + case MOVING_MOVING_BEHIND1 -> { + boolean cursorMoved = cursor1.move(); + if (cursorMoved) { + yield recalculateStateBasedOnCursorRelation(); + } else { + yield State.TERMINATED_MOVING; + } + } + case MOVING_MOVING_BEHIND2 -> { + boolean cursorMoved = cursor2.move(); + if (cursorMoved) { + yield recalculateStateBasedOnCursorRelation(); + } else { + yield State.MOVING_TERMINATED; + } + } + case TERMINATED_MOVING -> { + boolean cursorMoved = cursor2.move(); + if (cursorMoved) { + yield State.TERMINATED_MOVING; + } else { + yield State.TERMINATED_TERMINATED; + } + } + case MOVING_TERMINATED -> { + boolean cursorMoved = cursor1.move(); + if (cursorMoved) { + yield State.MOVING_TERMINATED; + } else { + yield State.TERMINATED_TERMINATED; + } + } + case MOVING_MOVING_HASH1 -> State.MOVING_MOVING_HASH2; + case TERMINATED_TERMINATED -> throw new IllegalStateException("Trying to move while terminated!"); + }; + } + + private State recalculateStateAfterCursorMovement(boolean cursor1Moved, boolean cursor2Moved) { + if (cursor1Moved && cursor2Moved) { + return recalculateStateBasedOnCursorRelation(); + } else if (cursor1Moved) { + return State.MOVING_TERMINATED; + } else if (cursor2Moved) { + return State.TERMINATED_MOVING; + } else { + return State.TERMINATED_TERMINATED; + } + } + + private State recalculateStateBasedOnCursorRelation() { + final int relation = InOrderMapCursor.comparePosition(cursor1, cursor2); + + if (relation > 0) { + return State.MOVING_MOVING_BEHIND1; + } else if (relation < 0) { + return State.MOVING_MOVING_BEHIND2; + } + + if (InOrderMapCursor.sameSubNode(cursor1, cursor2)) { + return State.MOVING_MOVING_SAME_NODE; + } else if (Objects.equals(cursor1.getKey(), cursor2.getKey())) { + if (Objects.equals(cursor1.getValue(), cursor2.getValue())) { + return State.MOVING_MOVING_SAME_KEY_SAME_VALUE; + } else { + return State.MOVING_MOVING_SAME_KEY_DIFFERENT_VALUE; + } + } + + final int depth = InOrderMapCursor.compareDepth(cursor1, cursor2); + + if (depth > 0) { + return State.MOVING_MOVING_BEHIND1; + } else if (depth < 0) { + return State.MOVING_MOVING_BEHIND2; + } else { + return State.MOVING_MOVING_HASH1; + } + + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MutableNode.java new file mode 100644 index 00000000..408ed62c --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/MutableNode.java @@ -0,0 +1,499 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +import tools.refinery.store.map.ContinuousHashProvider; + +import java.util.Arrays; +import java.util.Map; + +public class MutableNode extends Node { + int cachedHash; + protected boolean cachedHashValid; + protected Object[] content; + + protected MutableNode() { + this.content = new Object[2 * FACTOR]; + invalidateHash(); + } + + public static MutableNode initialize(K key, V value, ContinuousHashProvider hashProvider, V defaultValue) { + if (value == defaultValue) { + return null; + } else { + int hash = hashProvider.getHash(key, 0); + int fragment = hashFragment(hash, 0); + MutableNode res = new MutableNode<>(); + res.content[2 * fragment] = key; + res.content[2 * fragment + 1] = value; + res.invalidateHash(); + return res; + } + } + + /** + * Constructs a {@link MutableNode} as a copy of an {@link ImmutableNode} + * + * @param node to be transformed + */ + protected MutableNode(ImmutableNode node) { + this.content = new Object[2 * FACTOR]; + int dataUsed = 0; + int nodeUsed = 0; + for (int i = 0; i < FACTOR; i++) { + int bitPosition = 1 << i; + if ((node.dataMap & bitPosition) != 0) { + content[2 * i] = node.content[dataUsed * 2]; + content[2 * i + 1] = node.content[dataUsed * 2 + 1]; + dataUsed++; + } else if ((node.nodeMap & bitPosition) != 0) { + content[2 * i + 1] = node.content[node.content.length - 1 - nodeUsed]; + nodeUsed++; + } + } + this.cachedHashValid = false; + } + + @Override + public V getValue(K key, ContinuousHashProvider hashProvider, V defaultValue, int hash, int depth) { + int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); + @SuppressWarnings("unchecked") K keyCandidate = (K) this.content[2 * selectedHashFragment]; + if (keyCandidate != null) { + if (keyCandidate.equals(key)) { + @SuppressWarnings("unchecked") V value = (V) this.content[2 * selectedHashFragment + 1]; + return value; + } else { + return defaultValue; + } + } else { + @SuppressWarnings("unchecked") var nodeCandidate = (Node) content[2 * selectedHashFragment + 1]; + if (nodeCandidate != null) { + int newDepth = incrementDepth(depth); + int newHash = newHash(hashProvider, key, hash, newDepth); + return nodeCandidate.getValue(key, hashProvider, defaultValue, newHash, newDepth); + } else { + return defaultValue; + } + } + } + + @Override + public Node putValue(K key, V value, OldValueBox oldValueBox, ContinuousHashProvider hashProvider, V defaultValue, int hash, int depth) { + int selectedHashFragment = hashFragment(hash, shiftDepth(depth)); + @SuppressWarnings("unchecked") K keyCandidate = (K) content[2 * selectedHashFragment]; + if (keyCandidate != null) { + // If it has key + if (keyCandidate.equals(key)) { + // The key is equals to an existing key -> update entry + if (value == defaultValue) { + return removeEntry(selectedHashFragment, oldValueBox); + } else { + return updateValue(value, oldValueBox, selectedHashFragment); + } + } else { + // The key is not equivalent to an existing key on the same hash bin + // -> split entry if it is necessary + if (value == defaultValue) { + // Value is default -> do not need to add new node + oldValueBox.setOldValue(defaultValue); + return this; + } else { + // Value is not default -> Split entry data to a new node + oldValueBox.setOldValue(defaultValue); + return moveDownAndSplit(hashProvider, key, value, keyCandidate, hash, depth, selectedHashFragment); + } + } + } + // If it does not have key, check for value + @SuppressWarnings("unchecked") var nodeCandidate = (Node) content[2 * selectedHashFragment + 1]; + if (nodeCandidate != null) { + // If it has value, it is a sub-node -> update that + int newDepth = incrementDepth(depth); + var newNode = nodeCandidate.putValue(key, value, oldValueBox, hashProvider, defaultValue, newHash(hashProvider, key, hash, newDepth), newDepth); + return updateWithSubNode(selectedHashFragment, newNode, (value == null && defaultValue == null) || (value != null && value.equals(defaultValue))); + } else { + // If it does not have value, put it in the empty place + if (value == defaultValue) { + // don't need to add new key-value pair + oldValueBox.setOldValue(defaultValue); + return this; + } else { + return addEntry(key, value, oldValueBox, selectedHashFragment, defaultValue); + } + } + } + + private Node addEntry(K key, V value, OldValueBox oldValueBox, int selectedHashFragment, V defaultValue) { + content[2 * selectedHashFragment] = key; + oldValueBox.setOldValue(defaultValue); + content[2 * selectedHashFragment + 1] = value; + invalidateHash(); + return this; + } + + /** + * Updates an entry in a selected hash-fragment to a non-default value. + * + * @param value new value + * @param selectedHashFragment position of the value + * @return updated node + */ + @SuppressWarnings("unchecked") + Node updateValue(V value, OldValueBox oldValue, int selectedHashFragment) { + oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]); + content[2 * selectedHashFragment + 1] = value; + invalidateHash(); + return this; + } + + /** + * Updates an entry in a selected hash-fragment with a subtree. + * + * @param selectedHashFragment position of the value + * @param newNode the subtree + * @return updated node + */ + Node updateWithSubNode(int selectedHashFragment, Node newNode, boolean deletionHappened) { + if (deletionHappened) { + if (newNode == null) { + // Check whether this node become empty + content[2 * selectedHashFragment + 1] = null; // i.e. the new node + if (hasContent()) { + invalidateHash(); + return this; + } else { + return null; + } + } else { + // check whether newNode is orphan + MutableNode immutableNewNode = newNode.isMutable(); + if (immutableNewNode != null) { + int orphaned = immutableNewNode.isOrphaned(); + if (orphaned >= 0) { + // orphan sub-node data is replaced with data + content[2 * selectedHashFragment] = immutableNewNode.content[orphaned * 2]; + content[2 * selectedHashFragment + 1] = immutableNewNode.content[orphaned * 2 + 1]; + invalidateHash(); + return this; + } + } + } + } + // normal behaviour + content[2 * selectedHashFragment + 1] = newNode; + invalidateHash(); + return this; + } + + private boolean hasContent() { + for (Object element : this.content) { + if (element != null) return true; + } + return false; + } + + @Override + protected MutableNode isMutable() { + return this; + } + + protected int isOrphaned() { + int dataFound = -2; + for (int i = 0; i < FACTOR; i++) { + if (content[i * 2] != null) { + if (dataFound >= 0) { + return -1; + } else { + dataFound = i; + } + } else if (content[i * 2 + 1] != null) { + return -3; + } + } + return dataFound; + } + + @SuppressWarnings("unchecked") + private Node moveDownAndSplit(ContinuousHashProvider hashProvider, K newKey, V newValue, K previousKey, int hashOfNewKey, int depth, int selectedHashFragmentOfCurrentDepth) { + V previousValue = (V) content[2 * selectedHashFragmentOfCurrentDepth + 1]; + + MutableNode newSubNode = newNodeWithTwoEntries(hashProvider, previousKey, previousValue, hashProvider.getHash(previousKey, hashDepth(depth)), newKey, newValue, hashOfNewKey, incrementDepth(depth)); + + content[2 * selectedHashFragmentOfCurrentDepth] = null; + content[2 * selectedHashFragmentOfCurrentDepth + 1] = newSubNode; + invalidateHash(); + return this; + } + + // Pass everything as parameters for performance. + @SuppressWarnings("squid:S107") + private MutableNode newNodeWithTwoEntries(ContinuousHashProvider hashProvider, K key1, V value1, int oldHash1, K key2, V value2, int oldHash2, int newDepth) { + int newHash1 = newHash(hashProvider, key1, oldHash1, newDepth); + int newHash2 = newHash(hashProvider, key2, oldHash2, newDepth); + int newFragment1 = hashFragment(newHash1, shiftDepth(newDepth)); + int newFragment2 = hashFragment(newHash2, shiftDepth(newDepth)); + + MutableNode subNode = new MutableNode<>(); + if (newFragment1 != newFragment2) { + subNode.content[newFragment1 * 2] = key1; + subNode.content[newFragment1 * 2 + 1] = value1; + + subNode.content[newFragment2 * 2] = key2; + subNode.content[newFragment2 * 2 + 1] = value2; + } else { + MutableNode subSubNode = newNodeWithTwoEntries(hashProvider, key1, value1, newHash1, key2, value2, newHash2, incrementDepth(newDepth)); + subNode.content[newFragment1 * 2 + 1] = subSubNode; + } + subNode.invalidateHash(); + return subNode; + } + + @SuppressWarnings("unchecked") + Node removeEntry(int selectedHashFragment, OldValueBox oldValue) { + content[2 * selectedHashFragment] = null; + oldValue.setOldValue((V) content[2 * selectedHashFragment + 1]); + content[2 * selectedHashFragment + 1] = null; + if (hasContent()) { + invalidateHash(); + return this; + } else { + return null; + } + } + + @SuppressWarnings("unchecked") + @Override + public long getSize() { + int size = 0; + for (int i = 0; i < FACTOR; i++) { + if (content[i * 2] != null) { + size++; + } else { + Node nodeCandidate = (Node) content[i * 2 + 1]; + if (nodeCandidate != null) { + size += nodeCandidate.getSize(); + } + } + } + return size; + } + + @Override + protected MutableNode toMutable() { + return this; + } + + @Override + public ImmutableNode toImmutable(Map, ImmutableNode> cache) { + return ImmutableNode.constructImmutable(this, cache); + } + + @SuppressWarnings("unchecked") + @Override + boolean moveToNext(MapCursor cursor) { + // 1. try to move to data + if (cursor.dataIndex != MapCursor.INDEX_FINISH) { + for (int index = cursor.dataIndex + 1; index < FACTOR; index++) { + if (this.content[index * 2] != null) { + // 1.1 found next data + cursor.dataIndex = index; + cursor.key = (K) this.content[index * 2]; + cursor.value = (V) this.content[index * 2 + 1]; + return true; + } + } + cursor.dataIndex = MapCursor.INDEX_FINISH; + } + + // 2. look inside the sub-nodes + if(cursor.nodeIndexStack.peek()==null) { + throw new IllegalStateException("Cursor moved to the next state when the state is empty."); + } + for (int index = cursor.nodeIndexStack.peek() + 1; index < FACTOR; index++) { + if (this.content[index * 2] == null && this.content[index * 2 + 1] != null) { + // 2.1 found next sub-node, move down to the sub-node + Node subnode = (Node) this.content[index * 2 + 1]; + + cursor.dataIndex = MapCursor.INDEX_START; + cursor.nodeIndexStack.pop(); + cursor.nodeIndexStack.push(index); + cursor.nodeIndexStack.push(MapCursor.INDEX_START); + cursor.nodeStack.push(subnode); + + return subnode.moveToNext(cursor); + } + } + // 3. no sub-node found, move up + cursor.nodeStack.pop(); + cursor.nodeIndexStack.pop(); + if (!cursor.nodeStack.isEmpty()) { + Node supernode = cursor.nodeStack.peek(); + return supernode.moveToNext(cursor); + } else { + cursor.key = null; + cursor.value = null; + return false; + } + } + + @Override + @SuppressWarnings("unchecked") + boolean moveToNextInorder(InOrderMapCursor cursor) { + if(cursor.nodeIndexStack.peek()==null || cursor.nodeStack.peek()==null) { + throw new IllegalStateException("Cursor moved to the next state when the state is empty."); + } + + int position = cursor.nodeIndexStack.peek(); + + for (int index = position + 1; index < FACTOR; index++) { + // data found + if (this.content[index * 2] != null) { + cursor.nodeIndexStack.pop(); + cursor.nodeIndexStack.push(index); + + cursor.key = (K) this.content[index * 2]; + cursor.value = (V) this.content[index * 2 + 1]; + return true; + } else if (this.content[index * 2 +1] != null) { + // sub-node found + Node subnode = (Node) this.content[index * 2 +1]; + cursor.nodeIndexStack.pop(); + cursor.nodeIndexStack.push(index); + cursor.nodeIndexStack.push(InOrderMapCursor.INDEX_START); + cursor.nodeStack.push(subnode); + + return subnode.moveToNextInorder(cursor); + } + } + + // nothing found + cursor.nodeStack.pop(); + cursor.nodeIndexStack.pop(); + if (!cursor.nodeStack.isEmpty()) { + Node supernode = cursor.nodeStack.peek(); + return supernode.moveToNextInorder(cursor); + } else { + cursor.key = null; + cursor.value = null; + return false; + } + } + + @Override + public void prettyPrint(StringBuilder builder, int depth, int code) { + builder.append("\t".repeat(Math.max(0, depth))); + if (code >= 0) { + builder.append(code); + builder.append(":"); + } + builder.append("Mutable("); + // print content + boolean hadContent = false; + for (int i = 0; i < FACTOR; i++) { + if (content[2 * i] != null) { + if (hadContent) { + builder.append(","); + } + builder.append(i); + builder.append(":["); + builder.append(content[2 * i].toString()); + builder.append("]->["); + builder.append(content[2 * i + 1].toString()); + builder.append("]"); + hadContent = true; + } + } + builder.append(")"); + // print sub-nodes + for (int i = 0; i < FACTOR; i++) { + if (content[2 * i] == null && content[2 * i + 1] != null) { + @SuppressWarnings("unchecked") Node subNode = (Node) content[2 * i + 1]; + builder.append("\n"); + subNode.prettyPrint(builder, incrementDepth(depth), i); + } + } + } + + @Override + public void checkIntegrity(ContinuousHashProvider hashProvider, V defaultValue, int depth) { + // check for orphan nodes + if (depth > 0) { + int orphaned = isOrphaned(); + if (orphaned >= 0) { + throw new IllegalStateException("Orphaned node! " + orphaned + ": " + content[2 * orphaned]); + } + } + // check the place of data + for (int i = 0; i < FACTOR; i++) { + if (this.content[2 * i] != null) { + @SuppressWarnings("unchecked") K key = (K) this.content[2 * i]; + @SuppressWarnings("unchecked") V value = (V) this.content[2 * i + 1]; + + if (value == defaultValue) { + throw new IllegalStateException("Node contains default value!"); + } + int hashCode = hashProvider.getHash(key, hashDepth(depth)); + int shiftDepth = shiftDepth(depth); + int selectedHashFragment = hashFragment(hashCode, shiftDepth); + if (i != selectedHashFragment) { + throw new IllegalStateException("Key " + key + " with hash code " + hashCode + " is in bad place! Fragment=" + selectedHashFragment + ", Place=" + i); + } + } + } + // check sub-nodes + for (int i = 0; i < FACTOR; i++) { + if (this.content[2 * i + 1] != null && this.content[2 * i] == null) { + @SuppressWarnings("unchecked") var subNode = (Node) this.content[2 * i + 1]; + subNode.checkIntegrity(hashProvider, defaultValue, incrementDepth(depth)); + } + } + // check the hash + if (cachedHashValid) { + int oldHash = this.cachedHash; + invalidateHash(); + int newHash = hashCode(); + if (oldHash != newHash) { + throw new IllegalStateException("Hash code was not up to date! (old=" + oldHash + ",new=" + newHash + ")"); + } + } + } + + protected void invalidateHash() { + this.cachedHashValid = false; + } + + @Override + public int hashCode() { + if (!this.cachedHashValid) { + this.cachedHash = Arrays.hashCode(content); + this.cachedHashValid = true; + } + return this.cachedHash; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (obj instanceof MutableNode mutableObj) { + if (obj.hashCode() != this.hashCode()) { + return false; + } else { + for (int i = 0; i < FACTOR * 2; i++) { + Object thisContent = this.content[i]; + if (thisContent != null && !thisContent.equals(mutableObj.content[i])) { + return false; + } + } + return true; + } + } else if (obj instanceof ImmutableNode immutableObj) { + return ImmutableNode.compareImmutableMutable(immutableObj, this); + } else { + return false; + } + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/Node.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/Node.java new file mode 100644 index 00000000..0ecf2712 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/Node.java @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +import java.util.Map; + +import tools.refinery.store.map.ContinuousHashProvider; + +public abstract class Node { + public static final int BRANCHING_FACTOR_BITS = 5; + public static final int FACTOR = 1 << BRANCHING_FACTOR_BITS; + protected static final int NUMBER_OF_FACTORS = Integer.SIZE / BRANCHING_FACTOR_BITS; + protected static final int FACTOR_MASK = FACTOR - 1; + public static final int EFFECTIVE_BITS = BRANCHING_FACTOR_BITS * NUMBER_OF_FACTORS; + public static final int FACTOR_SELECTION_BITS = 32 - Integer.numberOfLeadingZeros(NUMBER_OF_FACTORS); + public static final int FACTOR_SELECTION_MASK = (1 << FACTOR_SELECTION_BITS) - 1; + public static final int INCREMENT_BIG_STEP = (1 << FACTOR_SELECTION_BITS) - NUMBER_OF_FACTORS; + + /** + * Increments the depth of the search in the tree. The depth parameter has two + * components: the least few bits selects the fragment of the hashcode, the + * other part selects the continuous hash. + * + * @param depth parameter encoding the fragment and the depth + * @return new depth. + */ + protected int incrementDepth(int depth) { + int newDepth = depth + 1; + if ((newDepth & FACTOR_SELECTION_MASK) == NUMBER_OF_FACTORS) { + newDepth += INCREMENT_BIG_STEP; + } + return newDepth; + } + + /** + * Calculates the index for the continuous hash. + * + * @param depth The depth of the node in the tree. + * @return The index of the continuous hash. + */ + protected static int hashDepth(int depth) { + return depth >> FACTOR_SELECTION_BITS; + } + + /** + * Calculates the which segment of a single hash should be used. + * + * @param depth The depth of the node in the tree. + * @return The segment of a hash code. + */ + protected static int shiftDepth(int depth) { + return depth & FACTOR_SELECTION_MASK; + } + + /** + * Selects a segments from a complete hash for a given depth. + * + * @param hash A complete hash. + * @param shiftDepth The index of the segment. + * @return The segment as an integer. + */ + protected static int hashFragment(int hash, int shiftDepth) { + if (shiftDepth < 0 || Node.NUMBER_OF_FACTORS < shiftDepth) + throw new IllegalArgumentException("Invalid shift depth! valid interval=[0;5], input=" + shiftDepth); + return (hash >>> shiftDepth * BRANCHING_FACTOR_BITS) & FACTOR_MASK; + } + + /** + * Returns the hash code for a given depth. It may calculate new hash code, or + * reuse a hash code calculated for depth-1. + * + * @param key The key. + * @param hash Hash code for depth-1 + * @param depth The depth. + * @return The new hash code. + */ + protected int newHash(final ContinuousHashProvider hashProvider, K key, int hash, int depth) { + final int shiftDepth = shiftDepth(depth); + if (shiftDepth == 0) { + final int hashDepth = hashDepth(depth); + if (hashDepth >= ContinuousHashProvider.MAX_PRACTICAL_DEPTH) { + throw new IllegalArgumentException( + "Key " + key + " have the clashing hashcode over the practical depth limitation (" + + ContinuousHashProvider.MAX_PRACTICAL_DEPTH + ")!"); + } + return hashProvider.getHash(key, hashDepth); + } else { + return hash; + } + } + + public abstract V getValue(K key, ContinuousHashProvider hashProvider, V defaultValue, int hash, + int depth); + + public abstract Node putValue(K key, V value, OldValueBox old, + ContinuousHashProvider hashProvider, V defaultValue, int hash, int depth); + + public abstract long getSize(); + + abstract MutableNode toMutable(); + + public abstract ImmutableNode toImmutable(Map, ImmutableNode> cache); + + protected abstract MutableNode isMutable(); + + /** + * Moves a {@link MapCursor} to its next position. + * + * @param cursor the cursor + * @return Whether there was a next value to move on. + */ + abstract boolean moveToNext(MapCursor cursor); + abstract boolean moveToNextInorder(InOrderMapCursor cursor); + + ///////// FOR printing + public abstract void prettyPrint(StringBuilder builder, int depth, int code); + + + @Override + public String toString() { + StringBuilder stringBuilder = new StringBuilder(); + prettyPrint(stringBuilder, 0, -1); + return stringBuilder.toString(); + } + + public void checkIntegrity(ContinuousHashProvider hashProvider, V defaultValue, int depth) { + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/OldValueBox.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/OldValueBox.java new file mode 100644 index 00000000..1b0a4b0c --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/OldValueBox.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +public class OldValueBox{ + V oldValue; + boolean isSet = false; + + public V getOldValue() { + if(!isSet) throw new IllegalStateException(); + isSet = false; + return oldValue; + } + + public void setOldValue(V ouldValue) { + if(isSet) throw new IllegalStateException(); + this.oldValue = ouldValue; + isSet = true; + } + +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java new file mode 100644 index 00000000..9409ace0 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java @@ -0,0 +1,38 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +import tools.refinery.store.map.*; + +import java.util.List; + +public class StateBasedVersionedMapStoreFactory implements VersionedMapStoreFactory { + private final V defaultValue; + private final ContinuousHashProvider continuousHashProvider; + private final VersionedMapStoreStateConfiguration config; + + public StateBasedVersionedMapStoreFactory(V defaultValue, Boolean transformToImmutable, VersionedMapStoreFactoryBuilder.SharingStrategy sharingStrategy, ContinuousHashProvider continuousHashProvider) { + this.defaultValue = defaultValue; + this.continuousHashProvider = continuousHashProvider; + + this.config = new VersionedMapStoreStateConfiguration( + transformToImmutable, + sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE || sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP, + sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP); + } + + @Override + public VersionedMapStore createOne() { + return new VersionedMapStoreStateImpl<>(continuousHashProvider, defaultValue, config); + + } + + @Override + public List> createGroup(int amount) { + return VersionedMapStoreStateImpl.createSharedVersionedMapStores(amount, continuousHashProvider, defaultValue, + config); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java new file mode 100644 index 00000000..817fc70b --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java @@ -0,0 +1,171 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +import tools.refinery.store.map.*; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Objects; + +/** + * Not threadSafe in itself + * + * @param + * @param + * @author Oszkar Semerath + */ +public class VersionedMapStateImpl implements VersionedMap { + protected final VersionedMapStoreStateImpl store; + + protected final ContinuousHashProvider hashProvider; + protected final V defaultValue; + protected Node root; + + private final OldValueBox oldValueBox = new OldValueBox<>(); + + public VersionedMapStateImpl( + VersionedMapStoreStateImpl store, + ContinuousHashProvider hashProvider, + V defaultValue) { + this.store = store; + this.hashProvider = hashProvider; + this.defaultValue = defaultValue; + this.root = null; + } + + public VersionedMapStateImpl( + VersionedMapStoreStateImpl store, + ContinuousHashProvider hashProvider, + V defaultValue, Node data) { + this.store = store; + this.hashProvider = hashProvider; + this.defaultValue = defaultValue; + this.root = data; + } + + @Override + public V getDefaultValue() { + return defaultValue; + } + + public ContinuousHashProvider getHashProvider() { + return hashProvider; + } + + @Override + public V put(K key, V value) { + if (root != null) { + root = root.putValue(key, value, oldValueBox, hashProvider, defaultValue, hashProvider.getHash(key, 0), 0); + return oldValueBox.getOldValue(); + } else { + root = MutableNode.initialize(key, value, hashProvider, defaultValue); + return defaultValue; + } + } + + @Override + public void putAll(Cursor cursor) { + if (cursor.getDependingMaps().contains(this)) { + List keys = new LinkedList<>(); + List values = new LinkedList<>(); + while (cursor.move()) { + keys.add(cursor.getKey()); + values.add(cursor.getValue()); + } + Iterator keyIterator = keys.iterator(); + Iterator valueIterator = values.iterator(); + while (keyIterator.hasNext()) { + var key = keyIterator.next(); + var value = valueIterator.next(); + this.put(key,value); + } + } else { + while (cursor.move()) { + this.put(cursor.getKey(), cursor.getValue()); + } + } + } + + @Override + public V get(K key) { + if (root != null) { + return root.getValue(key, hashProvider, defaultValue, hashProvider.getHash(key, 0), 0); + } else { + return defaultValue; + } + } + + @Override + public long getSize() { + if (root == null) { + return 0; + } else { + return root.getSize(); + } + } + + @Override + public Cursor getAll() { + return new MapCursor<>(this.root, this); + } + + @Override + public DiffCursor getDiffCursor(long toVersion) { + InOrderMapCursor fromCursor = new InOrderMapCursor<>(this); + VersionedMapStateImpl toMap = (VersionedMapStateImpl) this.store.createMap(toVersion); + InOrderMapCursor toCursor = new InOrderMapCursor<>(toMap); + return new MapDiffCursor<>(this.defaultValue, fromCursor, toCursor); + } + + + @Override + public long commit() { + return this.store.commit(root, this); + } + + public void setRoot(Node root) { + this.root = root; + } + + @Override + public void restore(long state) { + root = this.store.revert(state); + } + + public String prettyPrint() { + if (this.root != null) { + StringBuilder s = new StringBuilder(); + this.root.prettyPrint(s, 0, -1); + return s.toString(); + } else { + return "empty tree"; + } + } + + @Override + public void checkIntegrity() { + if (this.root != null) { + this.root.checkIntegrity(hashProvider, defaultValue, 0); + } + } + + @Override + public int contentHashCode(ContentHashCode mode) { + // Calculating the root hashCode is always fast, because {@link Node} caches its hashCode. + if(root == null) { + return 0; + } else { + return root.hashCode(); + } + } + + @Override + public boolean contentEquals(AnyVersionedMap other) { + return other instanceof VersionedMapStateImpl otherImpl && Objects.equals(root, otherImpl.root); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java new file mode 100644 index 00000000..45f30cc7 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +import tools.refinery.store.map.ContinuousHashProvider; +import tools.refinery.store.map.VersionedMapStore; + +public class VersionedMapStoreStateConfiguration { + + public VersionedMapStoreStateConfiguration() { + + } + public VersionedMapStoreStateConfiguration(boolean immutableWhenCommitting, boolean sharedNodeCacheInStore, + boolean sharedNodeCacheInStoreGroups) { + super(); + this.immutableWhenCommitting = immutableWhenCommitting; + this.sharedNodeCacheInStore = sharedNodeCacheInStore; + this.sharedNodeCacheInStoreGroups = sharedNodeCacheInStoreGroups; + } + + /** + * If true root is replaced with immutable node when committed. Frees up memory + * by releasing immutable nodes, but it may decrease performance by recreating + * immutable nodes upon changes (some evidence). + */ + private boolean immutableWhenCommitting = true; + public boolean isImmutableWhenCommitting() { + return immutableWhenCommitting; + } + + /** + * If true, all sub-nodes are cached within a {@link VersionedMapStore}. It + * decreases the memory requirements. It may increase performance by discovering + * existing immutable copy of a node (some evidence). Additional overhead may + * decrease performance (no example found). The option permits the efficient + * implementation of version deletion. + */ + private boolean sharedNodeCacheInStore = true; + public boolean isSharedNodeCacheInStore() { + return sharedNodeCacheInStore; + } + + /** + * If true, all sub-nodes are cached within a group of + * {@link VersionedMapStoreStateImpl#createSharedVersionedMapStores(int, ContinuousHashProvider, Object, VersionedMapStoreStateConfiguration)}. + * If {@link VersionedMapStoreStateConfiguration#sharedNodeCacheInStore} is + * false, then it has currently no impact. + */ + private boolean sharedNodeCacheInStoreGroups = true; + public boolean isSharedNodeCacheInStoreGroups() { + return sharedNodeCacheInStoreGroups; + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java new file mode 100644 index 00000000..0651772a --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java @@ -0,0 +1,129 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map.internal.state; + +import tools.refinery.store.map.*; + +import java.util.*; + +public class VersionedMapStoreStateImpl implements VersionedMapStore { + // Configuration + private final boolean immutableWhenCommitting; + + // Static data + protected final ContinuousHashProvider hashProvider; + protected final V defaultValue; + + // Dynamic data + protected final Map> states = new HashMap<>(); + protected final Map, ImmutableNode> nodeCache; + protected long nextID = 0; + + public VersionedMapStoreStateImpl(ContinuousHashProvider hashProvider, V defaultValue, + VersionedMapStoreStateConfiguration config) { + this.immutableWhenCommitting = config.isImmutableWhenCommitting(); + this.hashProvider = hashProvider; + this.defaultValue = defaultValue; + if (config.isSharedNodeCacheInStore()) { + nodeCache = new HashMap<>(); + } else { + nodeCache = null; + } + } + + private VersionedMapStoreStateImpl(ContinuousHashProvider hashProvider, V defaultValue, + Map, ImmutableNode> nodeCache, VersionedMapStoreStateConfiguration config) { + this.immutableWhenCommitting = config.isImmutableWhenCommitting(); + this.hashProvider = hashProvider; + this.defaultValue = defaultValue; + this.nodeCache = nodeCache; + } + + public VersionedMapStoreStateImpl(ContinuousHashProvider hashProvider, V defaultValue) { + this(hashProvider, defaultValue, new VersionedMapStoreStateConfiguration()); + } + + public static List> createSharedVersionedMapStores(int amount, + ContinuousHashProvider hashProvider, V defaultValue, + VersionedMapStoreStateConfiguration config) { + List> result = new ArrayList<>(amount); + if (config.isSharedNodeCacheInStoreGroups()) { + Map, ImmutableNode> nodeCache; + if (config.isSharedNodeCacheInStore()) { + nodeCache = new HashMap<>(); + } else { + nodeCache = null; + } + for (int i = 0; i < amount; i++) { + result.add(new VersionedMapStoreStateImpl<>(hashProvider, defaultValue, nodeCache, config)); + } + } else { + for (int i = 0; i < amount; i++) { + result.add(new VersionedMapStoreStateImpl<>(hashProvider, defaultValue, config)); + } + } + return result; + } + + public static List> createSharedVersionedMapStores(int amount, + ContinuousHashProvider hashProvider, V defaultValue) { + return createSharedVersionedMapStores(amount, hashProvider, defaultValue, new VersionedMapStoreStateConfiguration()); + } + + @Override + public synchronized Set getStates() { + return new HashSet<>(states.keySet()); + } + + @Override + public VersionedMap createMap() { + return new VersionedMapStateImpl<>(this, hashProvider, defaultValue); + } + + @Override + public VersionedMap createMap(long state) { + ImmutableNode data = revert(state); + return new VersionedMapStateImpl<>(this, hashProvider, defaultValue, data); + } + + public synchronized ImmutableNode revert(long state) { + if (states.containsKey(state)) { + return states.get(state); + } else { + ArrayList existingKeys = new ArrayList<>(states.keySet()); + Collections.sort(existingKeys); + throw new IllegalArgumentException("Store does not contain state " + state + "! Available states: " + + Arrays.toString(existingKeys.toArray())); + } + } + + public synchronized long commit(Node data, VersionedMapStateImpl mapToUpdateRoot) { + ImmutableNode immutable; + if (data != null) { + immutable = data.toImmutable(this.nodeCache); + } else { + immutable = null; + } + + if (nextID == Long.MAX_VALUE) + throw new IllegalStateException("Map store run out of Id-s"); + long id = nextID++; + this.states.put(id, immutable); + if (this.immutableWhenCommitting) { + mapToUpdateRoot.setRoot(immutable); + } + return id; + } + + @Override + public DiffCursor getDiffCursor(long fromState, long toState) { + VersionedMapStateImpl map1 = (VersionedMapStateImpl) createMap(fromState); + VersionedMapStateImpl map2 = (VersionedMapStateImpl) createMap(toState); + InOrderMapCursor cursor1 = new InOrderMapCursor<>(map1); + InOrderMapCursor cursor2 = new InOrderMapCursor<>(map2); + return new MapDiffCursor<>(this.defaultValue, cursor1, cursor2); + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java b/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java index fdd4425e..3c94541f 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProvider.java @@ -5,12 +5,12 @@ */ package tools.refinery.store.model; -import tools.refinery.store.map.ContinousHashProvider; +import tools.refinery.store.map.ContinuousHashProvider; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple1; import tools.refinery.store.tuple.Tuple2; -public class TupleHashProvider implements ContinousHashProvider { +public class TupleHashProvider implements ContinuousHashProvider { protected static final int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProviderBitMagic.java b/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProviderBitMagic.java deleted file mode 100644 index 14116a90..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/model/TupleHashProviderBitMagic.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.model; - -import tools.refinery.store.map.ContinousHashProvider; -import tools.refinery.store.tuple.Tuple; - -public class TupleHashProviderBitMagic implements ContinousHashProvider { - - @Override - public int getHash(Tuple key, int index) { - if(key.getSize() == 1) { - return key.get(0); - } - - int result = 0; - final int startBitIndex = index*30; - final int finalBitIndex = startBitIndex+30; - final int arity = key.getSize(); - - for(int i = startBitIndex; i<=finalBitIndex; i++) { - final int selectedKey = key.get(i%arity); - final int selectedPosition = 1<<(i/arity); - if((selectedKey&selectedPosition) != 0) { - result |= 1<<(i%30); - } - } - - return result; - } -} 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 aafbe130..2cc7b19c 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 @@ -8,10 +8,10 @@ package tools.refinery.store.model.internal; import tools.refinery.store.adapter.AdapterUtils; import tools.refinery.store.adapter.ModelAdapterBuilder; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreImpl; +import tools.refinery.store.map.VersionedMapStoreFactory; +import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.model.TupleHashProvider; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; @@ -77,8 +77,12 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { private void createStores(Map> stores, SymbolEquivalenceClass equivalenceClass, List symbols) { int size = symbols.size(); - var storeGroup = VersionedMapStoreImpl.createSharedVersionedMapStores(size, TupleHashProvider.INSTANCE, - equivalenceClass.defaultValue()); + VersionedMapStoreFactory mapFactory = VersionedMapStore + .builder() + .strategy(VersionedMapStoreFactoryBuilder.StoreStrategy.DELTA) + .defaultValue(equivalenceClass.defaultValue()) + .build(); + var storeGroup = mapFactory.createGroup(size); for (int i = 0; i < size; i++) { stores.put(symbols.get(i), storeGroup.get(i)); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java index 4ada4ea4..98756460 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/InOrderCursorTest.java @@ -8,8 +8,8 @@ package tools.refinery.store.map.tests; import org.junit.jupiter.api.Test; import tools.refinery.store.map.VersionedMapStore; import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; -import tools.refinery.store.map.internal.InOrderMapCursor; -import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.internal.state.InOrderMapCursor; +import tools.refinery.store.map.internal.state.VersionedMapStateImpl; import tools.refinery.store.map.tests.utils.MapTestEnvironment; import static org.junit.jupiter.api.Assertions.*; @@ -26,7 +26,7 @@ class InOrderCursorTest { .build() .createOne(); - VersionedMapImpl map = (VersionedMapImpl) store.createMap(); + VersionedMapStateImpl map = (VersionedMapStateImpl) store.createMap(); checkMove(map,0); map.put(1,"A"); @@ -44,7 +44,7 @@ class InOrderCursorTest { } - private void checkMove(VersionedMapImpl map, int num) { + private void checkMove(VersionedMapStateImpl map, int num) { InOrderMapCursor cursor = new InOrderMapCursor<>(map); for(int i=0; i store = new VersionedMapStoreImpl<>(TupleHashProvider.INSTANCE, false); + VersionedMapStore store = new VersionedMapStoreStateImpl<>(TupleHashProvider.INSTANCE, false); var map = store.createMap(); var out1 = map.put(Tuple.of(0), true); assertEquals(false, out1); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java index 420dade6..abfb4791 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MutableImmutableCompareFuzzTest.java @@ -17,10 +17,10 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import tools.refinery.store.map.ContinousHashProvider; +import tools.refinery.store.map.ContinuousHashProvider; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreImpl; -import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.internal.state.VersionedMapStoreStateImpl; +import tools.refinery.store.map.internal.state.VersionedMapStateImpl; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -28,11 +28,11 @@ class MutableImmutableCompareFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, boolean evilHash) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); + ContinuousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - VersionedMapStore store = new VersionedMapStoreImpl<>(chp, values[0]); - VersionedMapImpl immutable = (VersionedMapImpl) store.createMap(); - VersionedMapImpl mutable = (VersionedMapImpl) store.createMap(); + VersionedMapStore store = new VersionedMapStoreStateImpl<>(chp, values[0]); + VersionedMapStateImpl immutable = (VersionedMapStateImpl) store.createMap(); + VersionedMapStateImpl mutable = (VersionedMapStateImpl) store.createMap(); Random r = new Random(seed); @@ -40,8 +40,8 @@ class MutableImmutableCompareFuzzTest { commitFrequency); } - private void iterativeRandomPutsAndCommitsAndCompare(String scenario, VersionedMapImpl immutable, - VersionedMapImpl mutable, int steps, int maxKey, String[] values, Random r, + private void iterativeRandomPutsAndCommitsAndCompare(String scenario, VersionedMapStateImpl immutable, + VersionedMapStateImpl mutable, int steps, int maxKey, String[] values, Random r, int commitFrequency) { for (int i = 0; i < steps; i++) { int index = i + 1; diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java index 680d962d..b17766b7 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java @@ -18,10 +18,10 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import tools.refinery.store.map.ContinousHashProvider; +import tools.refinery.store.map.ContinuousHashProvider; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreImpl; -import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.internal.state.VersionedMapStoreStateImpl; +import tools.refinery.store.map.internal.state.VersionedMapStateImpl; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -31,9 +31,9 @@ class SharedStoreFuzzTest { private void runFuzzTest(String scenario, int seed, int steps, int maxKey, int maxValue, boolean nullDefault, int commitFrequency, boolean evilHash) { String[] values = MapTestEnvironment.prepareValues(maxValue, nullDefault); - ContinousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); + ContinuousHashProvider chp = MapTestEnvironment.prepareHashProvider(evilHash); - List> stores = VersionedMapStoreImpl.createSharedVersionedMapStores(5, chp, values[0]); + List> stores = VersionedMapStoreStateImpl.createSharedVersionedMapStores(5, chp, values[0]); iterativeRandomPutsAndCommitsThenRestore(scenario, stores, steps, maxKey, values, seed, commitFrequency); } @@ -42,9 +42,9 @@ class SharedStoreFuzzTest { int steps, int maxKey, String[] values, int seed, int commitFrequency) { // 1. maps with versions Random r = new Random(seed); - List> versioneds = new LinkedList<>(); + List> versioneds = new LinkedList<>(); for (VersionedMapStore store : stores) { - versioneds.add((VersionedMapImpl) store.createMap()); + versioneds.add((VersionedMapStateImpl) store.createMap()); } List> index2Version = new LinkedList<>(); @@ -66,9 +66,9 @@ class SharedStoreFuzzTest { } } // 2. create a non-versioned and - List> reference = new LinkedList<>(); + List> reference = new LinkedList<>(); for (VersionedMapStore store : stores) { - reference.add((VersionedMapImpl) store.createMap()); + reference.add((VersionedMapStateImpl) store.createMap()); } r = new Random(seed); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java index e7348370..401f2866 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java @@ -6,7 +6,7 @@ package tools.refinery.store.map.tests.utils; import tools.refinery.store.map.*; -import tools.refinery.store.map.internal.VersionedMapImpl; +import tools.refinery.store.map.internal.state.VersionedMapStateImpl; import java.util.*; import java.util.Map.Entry; @@ -28,7 +28,7 @@ public class MapTestEnvironment { return values; } - public static ContinousHashProvider prepareHashProvider(final boolean evil) { + public static ContinuousHashProvider prepareHashProvider(final boolean evil) { // Use maxPrime = 2147483629 return (key, index) -> { @@ -187,7 +187,7 @@ public class MapTestEnvironment { //System.out.println(cursor.getKey() + " " + ((VersionedMapImpl) versionedMap).getHashProvider() // .getHash(cursor.getKey(), 0)); if (previous != null) { - int comparisonResult = ((VersionedMapImpl) versionedMap).getHashProvider().compare(previous, + int comparisonResult = ((VersionedMapStateImpl) versionedMap).getHashProvider().compare(previous, cursor.getKey()); assertTrue(comparisonResult < 0, scenario + " Cursor order is not incremental!"); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/hashtests/HashEfficiencyTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/hashtests/HashEfficiencyTest.java index 4d4f5e26..5b595da7 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/model/hashtests/HashEfficiencyTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/model/hashtests/HashEfficiencyTest.java @@ -3,7 +3,7 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package tools.refinery.store.model.hashtests; +package tools.refinery.store.model.hashTests; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -14,10 +14,9 @@ import java.util.Random; import org.junit.jupiter.api.Test; -import tools.refinery.store.map.ContinousHashProvider; +import tools.refinery.store.map.ContinuousHashProvider; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.model.TupleHashProvider; -import tools.refinery.store.model.TupleHashProviderBitMagic; class HashEfficiencyTest { @@ -95,7 +94,7 @@ class HashEfficiencyTest { List p = nRandoms(2, amount, 1);; assertEquals(amount,p.size()); } - private static double calculateHashClashes(List tuples, ContinousHashProvider chp) { + private static double calculateHashClashes(List tuples, ContinuousHashProvider chp) { int sumClashes = 0; for(int i = 0; i chp, Tuple a, Tuple b) { + private static int calculateHashClash(ContinuousHashProvider chp, Tuple a, Tuple b) { if(a.equals(b)) return 0; final int bits = 5; final int segments = Integer.SIZE/bits; @@ -131,11 +130,9 @@ class HashEfficiencyTest { } public static void main(String[] args) { List hashNames = new LinkedList<>(); - List> hashes = new LinkedList<>(); + List> hashes = new LinkedList<>(); hashNames.add("PrimeGroup"); hashes.add(new TupleHashProvider()); - hashNames.add("BitMagic"); - hashes.add(new TupleHashProviderBitMagic()); int[] arities = new int[] {2,3,4,5}; int[] sizes = new int[] {32*32,32*32*8}; -- cgit v1.2.3-54-g00ecf From b34d844a40d0bc4095df6e4d7c4fad4eec2d919b Mon Sep 17 00:00:00 2001 From: nagilooh Date: Fri, 21 Jul 2023 16:49:53 +0200 Subject: Add Design space exploration and DFS strategy - Transformation rules - Design Space Exploration adapter - Depth First Strategy --- subprojects/store-query-viatra/build.gradle.kts | 1 + .../refinery/store/query/dse/ActionFactory.java | 9 + .../query/dse/DesignSpaceExplorationAdapter.java | 55 +++ .../query/dse/DesignSpaceExplorationBuilder.java | 55 +++ .../dse/DesignSpaceExplorationStoreAdapter.java | 6 + .../tools/refinery/store/query/dse/Strategy.java | 10 + .../store/query/dse/internal/Activation.java | 9 + .../DesignSpaceExplorationAdapterImpl.java | 256 +++++++++++++ .../DesignSpaceExplorationBuilderImpl.java | 77 ++++ .../DesignSpaceExplorationStoreAdapterImpl.java | 64 ++++ .../query/dse/internal/TransformationRule.java | 85 +++++ .../AlwaysSatisfiedDummyHardObjective.java | 51 +++ .../store/query/dse/objectives/BaseObjective.java | 147 ++++++++ .../store/query/dse/objectives/Comparators.java | 25 ++ .../store/query/dse/objectives/Fitness.java | 21 ++ .../dse/objectives/LeveledObjectivesHelper.java | 114 ++++++ .../store/query/dse/objectives/Objective.java | 110 ++++++ .../dse/objectives/ObjectiveComparatorHelper.java | 218 +++++++++++ .../query/dse/strategy/DepthFirstStrategy.java | 108 ++++++ .../query/dse/DesignSpaceExplorationTest.java | 117 ++++++ .../store/query/dse/TransformationRuleTest.java | 403 +++++++++++++++++++++ .../model/internal/ModelStoreBuilderImpl.java | 6 +- 22 files changed, 1944 insertions(+), 3 deletions(-) create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/ActionFactory.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationStoreAdapter.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/Strategy.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/Activation.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/AlwaysSatisfiedDummyHardObjective.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Comparators.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Fitness.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/LeveledObjectivesHelper.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/DepthFirstStrategy.java create mode 100644 subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java create mode 100644 subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/TransformationRuleTest.java (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/build.gradle.kts b/subprojects/store-query-viatra/build.gradle.kts index e3a22145..85ea7e2e 100644 --- a/subprojects/store-query-viatra/build.gradle.kts +++ b/subprojects/store-query-viatra/build.gradle.kts @@ -12,4 +12,5 @@ dependencies { implementation(libs.ecore) api(libs.viatra) api(project(":refinery-store-query")) + api(project(":refinery-store-reasoning")) } diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/ActionFactory.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/ActionFactory.java new file mode 100644 index 00000000..e2f452c3 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/ActionFactory.java @@ -0,0 +1,9 @@ +package tools.refinery.store.query.dse; + +import org.eclipse.collections.api.block.procedure.Procedure; +import tools.refinery.store.model.Model; +import tools.refinery.store.tuple.Tuple; + +public interface ActionFactory { + Procedure prepare(Model model); +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java new file mode 100644 index 00000000..e7ce7b2c --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java @@ -0,0 +1,55 @@ +package tools.refinery.store.query.dse; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.query.dse.internal.Activation; +import tools.refinery.store.query.dse.internal.DesignSpaceExplorationBuilderImpl; +import tools.refinery.store.query.dse.objectives.Fitness; +import tools.refinery.store.query.dse.objectives.ObjectiveComparatorHelper; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; + +import java.util.Collection; +import java.util.List; + +public interface DesignSpaceExplorationAdapter extends ModelAdapter { + @Override + DesignSpaceExplorationStoreAdapter getStoreAdapter(); + + static DesignSpaceExplorationBuilder builder() { + return new DesignSpaceExplorationBuilderImpl(); + } + + Collection explore(); + + public int getModelSize(); + + public Tuple1 createObject(); + + public Tuple deleteObject(Tuple tuple); + + public boolean checkGlobalConstraints(); + + public boolean backtrack(); + + public Fitness calculateFitness(); + + public void newSolution(); + + public int getDepth(); + + public Collection getUntraversedActivations(); + + public boolean fireActivation(Activation activation); + + public void fireRandomActivation(); + + public boolean isCurrentInTrajectory(); + + public List getTrajectory(); + + public boolean isCurrentStateAlreadyTraversed(); + + public ObjectiveComparatorHelper getObjectiveComparatorHelper(); + + public void restoreTrajectory(List trajectory); +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java new file mode 100644 index 00000000..0ec6f599 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java @@ -0,0 +1,55 @@ +package tools.refinery.store.query.dse; + +import tools.refinery.store.adapter.ModelAdapterBuilder; +import tools.refinery.store.query.dnf.AnyQuery; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.dse.internal.TransformationRule; +import tools.refinery.store.query.dse.objectives.Objective; + +import java.util.Collection; +import java.util.List; + +public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { + default DesignSpaceExplorationBuilder stopConditions(AnyQuery... stopConditions) { + return stopConditions(List.of(stopConditions)); + } + + default DesignSpaceExplorationBuilder stopConditions(Collection stopConditions) { + stopConditions.forEach(this::stopCondition); + return this; + } + + default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRules) { + return transformations(List.of(transformationRules)); + } + + default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { + transformationRules.forEach(this::transformation); + return this; + } + + default DesignSpaceExplorationBuilder globalConstraints(RelationalQuery... globalConstraints) { + return globalConstraints(List.of(globalConstraints)); + } + + default DesignSpaceExplorationBuilder globalConstraints(Collection globalConstraints) { + globalConstraints.forEach(this::globalConstraint); + return this; + } + + default DesignSpaceExplorationBuilder objectives(Objective... objectives) { + return objectives(List.of(objectives)); + } + + default DesignSpaceExplorationBuilder objectives(Collection objectives) { + objectives.forEach(this::objective); + return this; + } + + DesignSpaceExplorationBuilder stopCondition(AnyQuery stopCondition); + + DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule); + DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint); + DesignSpaceExplorationBuilder objective(Objective objective); + DesignSpaceExplorationBuilder strategy(Strategy strategy); +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationStoreAdapter.java new file mode 100644 index 00000000..21b10f0e --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationStoreAdapter.java @@ -0,0 +1,6 @@ +package tools.refinery.store.query.dse; + +import tools.refinery.store.adapter.ModelStoreAdapter; + +public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter { +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/Strategy.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/Strategy.java new file mode 100644 index 00000000..0aeea818 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/Strategy.java @@ -0,0 +1,10 @@ +package tools.refinery.store.query.dse; + +import tools.refinery.store.model.Model; + +public interface Strategy { + + public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter); + + public void explore(); +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/Activation.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/Activation.java new file mode 100644 index 00000000..82a4c978 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/Activation.java @@ -0,0 +1,9 @@ +package tools.refinery.store.query.dse.internal; + +import tools.refinery.store.tuple.Tuple; + +public record Activation(TransformationRule transformationRule, Tuple activation) { + public boolean fire() { + return transformationRule.fireActivation(activation); + } +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java new file mode 100644 index 00000000..5adf8306 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -0,0 +1,256 @@ +package tools.refinery.store.query.dse.internal; + +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; +import tools.refinery.store.query.dse.DesignSpaceExplorationStoreAdapter; +import tools.refinery.store.query.dse.Strategy; +import tools.refinery.store.query.dse.objectives.Fitness; +import tools.refinery.store.query.dse.objectives.LeveledObjectivesHelper; +import tools.refinery.store.query.dse.objectives.Objective; +import tools.refinery.store.query.dse.objectives.ObjectiveComparatorHelper; +import tools.refinery.store.query.resultset.ResultSet; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; + +import java.util.*; + +public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExplorationAdapter { + static final Symbol NODE_COUNT_SYMBOL = Symbol.of("MODEL_SIZE", 0, Integer.class, 0); + private final Model model; + private final ModelQueryAdapter queryEngine; + private final DesignSpaceExplorationStoreAdapterImpl storeAdapter; + private final Set transformationRules; + private final Set globalConstraints; + private final List objectives; + private final Set> globalConstraintResultSets = new HashSet<>(); + private final Interpretation sizeInterpretation; + private final Strategy strategy; + + private ObjectiveComparatorHelper objectiveComparatorHelper; + private List trajectory = new LinkedList<>(); + private Fitness lastFitness; + private final Set solutions = new HashSet<>(); +// private Map> statesAndActivations; + private Map> statesAndUntraversedActivations; + private Map> statesAndTraversedActivations; + private Random random = new Random(); + private Objective[][] leveledObjectives; + private boolean isNewState = false; + + public List getTrajectory() { + return trajectory; + } + + public DesignSpaceExplorationAdapterImpl(Model model, DesignSpaceExplorationStoreAdapterImpl storeAdapter) { + this.model = model; + this.storeAdapter = storeAdapter; + this.sizeInterpretation = model.getInterpretation(NODE_COUNT_SYMBOL); + queryEngine = model.getAdapter(ModelQueryAdapter.class); + + globalConstraints = storeAdapter.getGlobalConstraints(); + for (var constraint : globalConstraints) { + globalConstraintResultSets.add(queryEngine.getResultSet(constraint)); + } + + transformationRules = storeAdapter.getTransformationSpecifications(); + for (var rule : transformationRules) { + rule.prepare(model, queryEngine); + } + + objectives = storeAdapter.getObjectives(); + leveledObjectives = new LeveledObjectivesHelper(objectives).initLeveledObjectives(); + statesAndUntraversedActivations = new HashMap<>(); + statesAndTraversedActivations = new HashMap<>(); + strategy = storeAdapter.getStrategy(); + + } + + @Override + public Model getModel() { + return model; + } + + @Override + public DesignSpaceExplorationStoreAdapter getStoreAdapter() { + return storeAdapter; + } + + @Override + public Collection explore() { + var state = model.commit(); + trajectory.add(state); + statesAndUntraversedActivations.put(state, getAllActivations()); + statesAndTraversedActivations.put(state, new HashSet<>()); + strategy.initStrategy(this); + strategy.explore(); + return solutions; + } + + @Override + public int getModelSize() { + return sizeInterpretation.get(Tuple.of()); + } + + @Override + public Tuple1 createObject() { + var newNodeId = getModelSize(); + sizeInterpretation.put(Tuple.of(), newNodeId + 1); + return Tuple.of(newNodeId); + } + + @Override + public Tuple deleteObject(Tuple tuple) { + if (tuple.getSize() != 1) { + throw new IllegalArgumentException("Tuple size must be 1"); + } +// TODO: implement more efficient deletion +// if (tuple.get(0) == getModelSize() - 1) { +// sizeInterpretation.put(Tuple.of(), getModelSize() - 1); +// } + return tuple; + } + + @Override + public boolean checkGlobalConstraints() { + for (var resultSet : globalConstraintResultSets) { + if (resultSet.size() > 0) { + return false; + } + } + return true; + } + + @Override + public boolean backtrack() { + if (trajectory.size() < 2) { + return false; + } + model.restore(trajectory.get(trajectory.size() - 2)); + trajectory.remove(trajectory.size() - 1); + return true; + } + + @Override + public void restoreTrajectory(List trajectory) { + model.restore(trajectory.get(trajectory.size() - 1)); + this.trajectory = trajectory; + + } + + @Override + public Fitness calculateFitness() { + Fitness result = new Fitness(); + boolean satisfiesHardObjectives = true; + for (Objective objective : objectives) { + var fitness = objective.getFitness(this); + result.put(objective.getName(), fitness); + if (objective.isHardObjective() && !objective.satisfiesHardObjective(fitness)) { + satisfiesHardObjectives = false; + } + } + result.setSatisfiesHardObjectives(satisfiesHardObjectives); + + lastFitness = result; + + return result; + } + + @Override + public void newSolution() { + var state = model.getState(); + solutions.add(state); + } + + @Override + public int getDepth() { + return trajectory.size() - 1; + } + + public Collection getUntraversedActivations() { +// return statesAndUntraversedActivations.get(model.getState()); + List untraversedActivations = new ArrayList<>(); + for (Activation activation : getAllActivations()) { + if (!statesAndTraversedActivations.get(model.getState()).contains(activation)) { + untraversedActivations.add(activation); + } + } + + return untraversedActivations; + } + + @Override + public boolean fireActivation(Activation activation) { + if (activation == null) { + return false; + } + long previousState = model.getState(); + if (!statesAndUntraversedActivations.get(previousState).contains(activation)) { +// TODO: throw exception? + return false; + } + if (!activation.fire()) { + return false; + } + statesAndUntraversedActivations.get(previousState).remove(activation); + statesAndTraversedActivations.get(previousState).add(activation); + long newState = model.commit(); + trajectory.add(newState); + isNewState = !statesAndUntraversedActivations.containsKey(newState); + statesAndUntraversedActivations.put(newState, getAllActivations()); + statesAndTraversedActivations.put(newState, new HashSet<>()); + return true; + } + + @Override + public void fireRandomActivation() { + var activations = getUntraversedActivations(); + if (activations.isEmpty()) { +// TODO: throw exception + return; + } + int index = random.nextInt(activations.size()); + var iterator = activations.iterator(); + while (index-- > 0) { + iterator.next(); + } + var activationId = iterator.next(); + fireActivation(activationId); + } + + @Override + public boolean isCurrentInTrajectory() { + return trajectory.contains(model.getState()); + } + + public Collection getAllActivations() { + Collection result = new HashSet<>(); + for (var rule : transformationRules) { + result.addAll(rule.getAllActivations()); + } + return result; + } + + public boolean isCurrentStateAlreadyTraversed() { +// TODO: check isomorphism? + return !isNewState; + } + + public Fitness getLastFitness() { + return lastFitness; + } + + public ObjectiveComparatorHelper getObjectiveComparatorHelper() { + if (objectiveComparatorHelper == null) { + objectiveComparatorHelper = new ObjectiveComparatorHelper(leveledObjectives); + } + return objectiveComparatorHelper; + } + + public Objective[][] getLeveledObjectives() { + return leveledObjectives; + } +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java new file mode 100644 index 00000000..79864de0 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java @@ -0,0 +1,77 @@ +package tools.refinery.store.query.dse.internal; + +import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.dnf.AnyQuery; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.dse.DesignSpaceExplorationBuilder; +import tools.refinery.store.query.dse.Strategy; +import tools.refinery.store.query.dse.objectives.Objective; + +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class DesignSpaceExplorationBuilderImpl + extends AbstractModelAdapterBuilder + implements DesignSpaceExplorationBuilder { + + private final Set stopConditionSpecifications = new LinkedHashSet<>(); + private final Set transformationSpecifications = new LinkedHashSet<>(); + private final Set globalConstraints = new LinkedHashSet<>(); + private final List objectives = new LinkedList<>(); + private Strategy strategy; + + @Override + protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) { + return new DesignSpaceExplorationStoreAdapterImpl(store, stopConditionSpecifications, + transformationSpecifications, globalConstraints, objectives, strategy); + } + + @Override + public DesignSpaceExplorationBuilder stopCondition(AnyQuery stopCondition) { + checkNotConfigured(); + stopConditionSpecifications.add(stopCondition); + return this; + } + + @Override + public DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule) { + checkNotConfigured(); + transformationSpecifications.add(transformationRule); + return this; + } + + @Override + public DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint) { + checkNotConfigured(); + globalConstraints.add(globalConstraint); + return this; + } + + @Override + public DesignSpaceExplorationBuilder objective(Objective objective) { + checkNotConfigured(); + objectives.add(objective); + return this; + } + + @Override + public DesignSpaceExplorationBuilder strategy(Strategy strategy) { + checkNotConfigured(); + this.strategy = strategy; + return this; + } + + public Set getStopConditionSpecifications() { + return stopConditionSpecifications; + } + + @Override + protected void doConfigure(ModelStoreBuilder storeBuilder) { + storeBuilder.symbols(DesignSpaceExplorationAdapterImpl.NODE_COUNT_SYMBOL); + super.doConfigure(storeBuilder); + } +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java new file mode 100644 index 00000000..9c2fa4d6 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java @@ -0,0 +1,64 @@ +package tools.refinery.store.query.dse.internal; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.query.dnf.AnyQuery; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.dse.DesignSpaceExplorationStoreAdapter; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.dse.Strategy; +import tools.refinery.store.query.dse.objectives.Objective; + +import java.util.List; +import java.util.Set; + +public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { + private final ModelStore store; + private final Set stopConditionSpecifications; + private final Set transformationSpecifications; + private final Set globalConstraints; + private final List objectives; + private final Strategy strategy; + + public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, Set stopConditionSpecifications, + Set transformationSpecifications, + Set globalConstraints, List objectives, + Strategy strategy) { + this.store = store; + this.stopConditionSpecifications = stopConditionSpecifications; + this.transformationSpecifications = transformationSpecifications; + this.globalConstraints = globalConstraints; + this.objectives = objectives; + this.strategy = strategy; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public ModelAdapter createModelAdapter(Model model) { + return new DesignSpaceExplorationAdapterImpl(model, this); + } + + public Set getStopConditionSpecifications() { + return stopConditionSpecifications; + } + + public Set getTransformationSpecifications() { + return transformationSpecifications; + } + + public Set getGlobalConstraints() { + return globalConstraints; + } + + public List getObjectives() { + return objectives; + } + + public Strategy getStrategy() { + return strategy; + } +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java new file mode 100644 index 00000000..c46ff712 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java @@ -0,0 +1,85 @@ +package tools.refinery.store.query.dse.internal; + +import org.eclipse.collections.api.block.procedure.Procedure; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.dse.ActionFactory; +import tools.refinery.store.query.resultset.OrderedResultSet; +import tools.refinery.store.query.resultset.ResultSet; +import tools.refinery.store.tuple.Tuple; + +import java.util.HashSet; +import java.util.Random; +import java.util.Set; + +public class TransformationRule { + + private final String name; + private final RelationalQuery precondition; + private final ActionFactory actionFactory; + private Procedure action; + private OrderedResultSet activations; + private Random random; + private ModelQueryAdapter queryEngine; + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) { + this(name, precondition, actionFactory, new Random()); + } + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) { + this(name, precondition, actionFactory, new Random(seed)); + } + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) { + this.name = name; + this.precondition = precondition; + this.actionFactory = actionFactory; + this.random = random; + } + public boolean prepare(Model model, ModelQueryAdapter queryEngine) { + action = actionFactory.prepare(model); + this.queryEngine = queryEngine; + activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); + return true; + } + + public boolean fireActivation(Tuple activation) { + action.accept(activation); + queryEngine.flushChanges(); + return true; + } + + public boolean fireRandomActivation() { + return getRandomActivation().fire(); + } + + public String getName() { + return name; + } + + public RelationalQuery getPrecondition() { + return precondition; + } + + public ResultSet getAllActivationsAsSets() { + return activations; + } + + public Set getAllActivations() { + var result = new HashSet(); + var cursor = activations.getAll(); + while (cursor.move()) { + result.add(new Activation(this, cursor.getKey())); + } + return result; + } + + public Activation getRandomActivation() { + return new Activation(this, activations.getKey(random.nextInt(activations.size()))); + } + + public Activation getActivation(int index) { + return new Activation(this, activations.getKey(index)); + } +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/AlwaysSatisfiedDummyHardObjective.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/AlwaysSatisfiedDummyHardObjective.java new file mode 100644 index 00000000..26744d94 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/AlwaysSatisfiedDummyHardObjective.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.query.dse.objectives; + +import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; + +/** + * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution. + * + * @author Andras Szabolcs Nagy + * + */ +public class AlwaysSatisfiedDummyHardObjective extends BaseObjective { + + private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective"; + + public AlwaysSatisfiedDummyHardObjective() { + super(DEFAULT_NAME); + } + + public AlwaysSatisfiedDummyHardObjective(String name) { + super(name); + } + + @Override + public Double getFitness(DesignSpaceExplorationAdapter context) { + return 0d; + } + + @Override + public boolean isHardObjective() { + return true; + } + + @Override + public boolean satisfiesHardObjective(Double fitness) { + return true; + } + + @Override + public Objective createNew() { + return this; + } + +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java new file mode 100644 index 00000000..fd8529f7 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.query.dse.objectives; + +import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; + +import java.util.Comparator; +import java.util.Objects; + +/** + * This abstract class implements the basic functionality of an objective ({@link Objective} namely its name, + * comparator, level and fitness hard constraint. + * + * @author Andras Szabolcs Nagy + * + */ +public abstract class BaseObjective implements Objective { + + protected final String name; + protected Comparator comparator = Comparators.HIGHER_IS_BETTER; + protected int level = 0; + + protected double fitnessConstraint; + protected boolean isThereFitnessConstraint = false; + protected Comparator fitnessConstraintComparator; + + public BaseObjective(String name) { + Objects.requireNonNull(name, "Name of the objective cannot be null."); + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setComparator(Comparator comparator) { + this.comparator = comparator; + } + + @Override + public Comparator getComparator() { + return comparator; + } + + @Override + public void setLevel(int level) { + this.level = level; + } + + @Override + public int getLevel() { + return level; + } + + public BaseObjective withLevel(int level) { + setLevel(level); + return this; + } + + public BaseObjective withComparator(Comparator comparator) { + setComparator(comparator); + return this; + } + + /** + * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the + * current state as a solution. + * + * @param fitnessConstraint + * Solutions should be better than this value. + * @param fitnessConstraintComparator + * {@link Comparator} to determine if the current state is better than the given value. + * @return The actual instance to enable builder pattern like usage. + */ + public BaseObjective withHardConstraintOnFitness(double fitnessConstraint, + Comparator fitnessConstraintComparator) { + this.fitnessConstraint = fitnessConstraint; + this.fitnessConstraintComparator = fitnessConstraintComparator; + this.isThereFitnessConstraint = true; + return this; + } + + /** + * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the + * current state as a solution. The provided comparator will be used. + * + * @param fitnessConstraint + * Solutions should be better than this value. + * @return The actual instance to enable builder pattern like usage. + */ + public BaseObjective withHardConstraintOnFitness(double fitnessConstraint) { + return withHardConstraintOnFitness(fitnessConstraint, null); + } + + @Override + public void init(DesignSpaceExplorationAdapter context) { + if (fitnessConstraintComparator == null) { + fitnessConstraintComparator = comparator; + } + } + + @Override + public boolean isHardObjective() { + return isThereFitnessConstraint; + } + + @Override + public boolean satisfiesHardObjective(Double fitness) { + if (isThereFitnessConstraint) { + int compare = fitnessConstraintComparator.compare(fitness, fitnessConstraint); + if (compare < 0) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof BaseObjective baseObjective) { + return name.equals(baseObjective.getName()); + } + return false; + } + + @Override + public String toString() { + return name; + } + +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Comparators.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Comparators.java new file mode 100644 index 00000000..7fba6736 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Comparators.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.query.dse.objectives; + +import java.util.Comparator; + +public class Comparators { + + private Comparators() { /*Utility class constructor*/ } + + public static final Comparator HIGHER_IS_BETTER = (o1, o2) -> o1.compareTo(o2); + + public static final Comparator LOWER_IS_BETTER = (o1, o2) -> o2.compareTo(o1); + + private static final Double ZERO = (double) 0; + + public static final Comparator DIFFERENCE_TO_ZERO_IS_BETTER = (o1, o2) -> ZERO.compareTo(Math.abs(o1)-Math.abs(o2)); + +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Fitness.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Fitness.java new file mode 100644 index 00000000..ea80ed55 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Fitness.java @@ -0,0 +1,21 @@ +package tools.refinery.store.query.dse.objectives; + +import java.util.HashMap; + +public class Fitness extends HashMap { + + private boolean satisfiesHardObjectives; + + public boolean isSatisfiesHardObjectives() { + return satisfiesHardObjectives; + } + + public void setSatisfiesHardObjectives(boolean satisfiesHardObjectives) { + this.satisfiesHardObjectives = satisfiesHardObjectives; + } + + @Override + public String toString() { + return super.toString() + " hardObjectives=" + satisfiesHardObjectives; + } +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/LeveledObjectivesHelper.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/LeveledObjectivesHelper.java new file mode 100644 index 00000000..6c4e7837 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/LeveledObjectivesHelper.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.query.dse.objectives; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +public class LeveledObjectivesHelper { + + private List objectives = new ArrayList(); + private Objective[][] leveledObjectives; + + public LeveledObjectivesHelper(List objectives) { + this.objectives = objectives; + } + + public Objective[][] initLeveledObjectives() { + if (objectives.isEmpty()) { + leveledObjectives = new Objective[0][0]; + return leveledObjectives; + } + + int level = objectives.get(0).getLevel(); + boolean oneLevelOnly = true; + for (Objective objective : objectives) { + if (objective.getLevel() != level) { + oneLevelOnly = false; + break; + } + } + + if (oneLevelOnly) { + leveledObjectives = new Objective[1][objectives.size()]; + for (int i = 0; i < objectives.size(); i++) { + leveledObjectives[0][i] = objectives.get(i); + } + return leveledObjectives; + } + + Objective[] objectivesArray = getSortedByLevelObjectives(objectives); + + int numberOfLevels = getNumberOfObjectiveLevels(objectivesArray); + + leveledObjectives = new Objective[numberOfLevels][]; + + fillLeveledObjectives(objectivesArray); + + return leveledObjectives; + } + + private void fillLeveledObjectives(Objective[] objectivesArray) { + int actLevel = objectivesArray[0].getLevel(); + int levelIndex = 0; + int lastIndex = 0; + int corrigationForLastLevel = 0; + boolean oneObjectiveAtLastLevel = false; + for (int i = 0; i < objectivesArray.length; i++) { + if (i == objectivesArray.length - 1) { + corrigationForLastLevel = 1; + if (objectivesArray[i - 1].getLevel() != objectivesArray[i].getLevel()) { + oneObjectiveAtLastLevel = true; + corrigationForLastLevel = 0; + } + } + if (objectivesArray[i].getLevel() != actLevel || corrigationForLastLevel == 1 || oneObjectiveAtLastLevel) { + leveledObjectives[levelIndex] = new Objective[i - lastIndex + corrigationForLastLevel]; + for (int j = lastIndex; j < i + corrigationForLastLevel; j++) { + leveledObjectives[levelIndex][j - lastIndex] = objectivesArray[j]; + } + if (oneObjectiveAtLastLevel) { + leveledObjectives[levelIndex + 1] = new Objective[1]; + leveledObjectives[levelIndex + 1][0] = objectivesArray[i]; + } + actLevel = objectivesArray[i].getLevel(); + levelIndex++; + lastIndex = i; + } + } + } + + private int getNumberOfObjectiveLevels(Objective[] objectivesArray) { + + int actLevel = objectivesArray[0].getLevel(); + int numberOfLevels = 1; + + for (int i = 1; i < objectivesArray.length; i++) { + if (objectivesArray[i].getLevel() != actLevel) { + numberOfLevels++; + actLevel = objectivesArray[i].getLevel(); + } + } + + return numberOfLevels; + } + + private Objective[] getSortedByLevelObjectives(List objectives) { + Objective[] objectivesArray = objectives.toArray(new Objective[objectives.size()]); + Arrays.sort(objectivesArray, Comparator.comparingInt(Objective::getLevel)); + return objectivesArray; + } + + public Objective[][] getLeveledObjectives() { + return leveledObjectives; + } + +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java new file mode 100644 index 00000000..b160b453 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.query.dse.objectives; + +import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; + +import java.util.Comparator; + +/** + * + * Implementation of this interface represents a single objective of the DSE problem, which can assess a solution + * (trajectory) in a single number. It has a name and a comparator which orders two solution based on the calculated + * value. + *

+ * Objectives can be either hard or soft objectives. Hard objectives can be satisfied or unsatisfied. If all of the hard + * objectives are satisfied on a single solution, then it is considered to be a valid (or goal) solution. + *

+ * Certain objectives can have inner state for calculating the fitness value. In this case a new instance is necessary + * for every new thread, and the {@code createNew} method should not return the same instance more than once. + * + * @author Andras Szabolcs Nagy + * + */ +public interface Objective { + + /** + * Returns the name of the objective. + * + * @return The name of the objective. + */ + String getName(); + + /** + * Sets the {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is to + * minimize or maximize (or minimize or maximize a delta from a given number). + * + * @param comparator The comparator. + */ + void setComparator(Comparator comparator); + + /** + * Returns a {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is + * to minimize or maximize (or minimize or maximize a delta from a given number). + * + * @return The comparator. + */ + Comparator getComparator(); + + /** + * Calculates the value of the objective on a given solution (trajectory). + * + * @param context + * The {@link DesignSpaceExplorationAdapter} + * @return The objective value in double. + */ + Double getFitness(DesignSpaceExplorationAdapter context); + + /** + * Initializes the objective. It is called exactly once for every thread starts. + * + * @param context + * The {@link DesignSpaceExplorationAdapter}. + */ + void init(DesignSpaceExplorationAdapter context); + + /** + * Returns an instance of the {@link Objective}. If it returns the same instance, all the methods has to be thread + * save as they are called concurrently. + * + * @return An instance of the objective. + */ + Objective createNew(); + + /** + * Returns true if the objective is a hard objective. In such a case the method + * {@link Objective#satisfiesHardObjective(Double)} is called. + * + * @return True if the objective is a hard objective. + * @see Objective#satisfiesHardObjective(Double) + * @see Objective + */ + boolean isHardObjective(); + + /** + * Determines if the given fitness value satisfies the hard objective. + * + * @param fitness + * The fitness value of a solution. + * @return True if it satisfies the hard objective or it is a soft constraint. + * @see Objective + */ + boolean satisfiesHardObjective(Double fitness); + + /** + * Set the level of the objective. + */ + void setLevel(int level); + + /** + * Gets the level of the objective. + */ + int getLevel(); + +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java new file mode 100644 index 00000000..23fb24ee --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.query.dse.objectives; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; + +/** + * This class is responsible to compare and sort fitness values. {@link TrajectoryFitness} instances can be added to an + * instance of this class, that it can sort them. + * + * @author András Szabolcs Nagy + */ +public class ObjectiveComparatorHelper { + + private Objective[][] leveledObjectives; + private List trajectoryFitnesses = new ArrayList(); + private Random random = new Random(); + private boolean computeCrowdingDistance = false; + + public ObjectiveComparatorHelper(Objective[][] leveledObjectives) { + this.leveledObjectives = leveledObjectives; + } + + public void setComputeCrowdingDistance(boolean computeCrowdingDistance) { + this.computeCrowdingDistance = computeCrowdingDistance; + } + + /** + * Compares two fitnesses based on hierarchical dominance. Returns -1 if the second parameter {@code o2} is a better + * solution ({@code o2} dominates {@code o1}), 1 if the first parameter {@code o1} is better ({@code o1} dominates + * {@code o2}) and returns 0 if they are non-dominating each other. + */ + public int compare(Fitness o1, Fitness o2) { + + levelsLoop: + for (Objective[] leveledObjective : leveledObjectives) { + + boolean o1HasBetterFitness = false; + boolean o2HasBetterFitness = false; + + for (Objective objective : leveledObjective) { + String objectiveName = objective.getName(); + int sgn = objective.getComparator().compare(o1.get(objectiveName), o2.get(objectiveName)); + + if (sgn < 0) { + o2HasBetterFitness = true; + } + if (sgn > 0) { + o1HasBetterFitness = true; + } + if (o1HasBetterFitness && o2HasBetterFitness) { + continue levelsLoop; + } + } + if (o2HasBetterFitness) { + return -1; + } else if (o1HasBetterFitness) { + return 1; + } + } + + return 0; + + } + + /** + * Adds a {@link TrajectoryFitness} to an inner list to compare later. + * + * @param trajectoryFitness + */ + public void addTrajectoryFitness(TrajectoryFitness trajectoryFitness) { + trajectoryFitnesses.add(trajectoryFitness); + } + + /** + * Clears the inner {@link TrajectoryFitness} list. + */ + public void clearTrajectoryFitnesses() { + trajectoryFitnesses.clear(); + } + + /** + * Returns the inner {@link TrajectoryFitness} list. + */ + public List getTrajectoryFitnesses() { + return trajectoryFitnesses; + } + + /** + * Returns a random {@link TrajectoryFitness} from the pareto front. + */ + public TrajectoryFitness getRandomBest() { + List paretoFront = getParetoFront(); + int randomIndex = random.nextInt(paretoFront.size()); + return paretoFront.get(randomIndex); + } + + /** + * Returns the pareto front of the previously added {@link TrajectoryFitness}. + */ + public List getParetoFront() { + return getFronts().get(0); + } + + /** + * Returns the previously added {@link TrajectoryFitness} instances in fronts. + */ + public List> getFronts() { + Preconditions.checkArgument(!trajectoryFitnesses.isEmpty(), "No trajectory fitnesses were added."); + List> fronts = new ArrayList>(); + + Map> dominatedInstances = new HashMap>(); + Map dominatingInstances = new HashMap(); + + // calculate dominations + for (TrajectoryFitness TrajectoryFitnessP : trajectoryFitnesses) { + dominatedInstances.put(TrajectoryFitnessP, new ArrayList()); + dominatingInstances.put(TrajectoryFitnessP, 0); + + for (TrajectoryFitness TrajectoryFitnessQ : trajectoryFitnesses) { + int dominates = compare(TrajectoryFitnessP.fitness, TrajectoryFitnessQ.fitness); + if (dominates > 0) { + dominatedInstances.get(TrajectoryFitnessP).add(TrajectoryFitnessQ); + } else if (dominates < 0) { + dominatingInstances.put(TrajectoryFitnessP, dominatingInstances.get(TrajectoryFitnessP) + 1); + } + } + + if (dominatingInstances.get(TrajectoryFitnessP) == 0) { + // p belongs to the first front + TrajectoryFitnessP.rank = 1; + if (fronts.isEmpty()) { + ArrayList firstDominationFront = new ArrayList(); + firstDominationFront.add(TrajectoryFitnessP); + fronts.add(firstDominationFront); + } else { + List firstDominationFront = fronts.get(0); + firstDominationFront.add(TrajectoryFitnessP); + } + } + } + + // create fronts + int i = 1; + while (fronts.size() == i) { + ArrayList nextDominationFront = new ArrayList(); + for (TrajectoryFitness TrajectoryFitnessP : fronts.get(i - 1)) { + for (TrajectoryFitness TrajectoryFitnessQ : dominatedInstances.get(TrajectoryFitnessP)) { + dominatingInstances.put(TrajectoryFitnessQ, dominatingInstances.get(TrajectoryFitnessQ) - 1); + if (dominatingInstances.get(TrajectoryFitnessQ) == 0) { + TrajectoryFitnessQ.rank = i + 1; + nextDominationFront.add(TrajectoryFitnessQ); + } + } + } + i++; + if (!nextDominationFront.isEmpty()) { + if (computeCrowdingDistance) { + crowdingDistanceAssignment(nextDominationFront, leveledObjectives); + } + fronts.add(nextDominationFront); + } + } + + return fronts; + } + + /** + * Executes the crowding distance assignment for the specified front. + * + * @param front + */ + public static void crowdingDistanceAssignment(List front, Objective[][] leveledObjectives) { + + for (TrajectoryFitness InstanceData : front) { + // initialize crowding distance + InstanceData.crowdingDistance = 0; + } + + for (final Objective[] objectives : leveledObjectives) { + for (final Objective objective : objectives) { + + final String m = objective.getName(); + TrajectoryFitness[] sortedFront = front.toArray(new TrajectoryFitness[0]); + // sort using m-th objective value + Arrays.sort(sortedFront, (o1, o2) -> objective.getComparator().compare(o1.fitness.get(m), o2.fitness.get(m))); + // so that boundary points are always selected + sortedFront[0].crowdingDistance = Double.POSITIVE_INFINITY; + sortedFront[sortedFront.length - 1].crowdingDistance = Double.POSITIVE_INFINITY; + // If minimal and maximal fitness value for this objective are + // equal, then do not change crowding distance + if (sortedFront[0].fitness.get(m) != sortedFront[sortedFront.length - 1].fitness.get(m)) { + for (int i = 1; i < sortedFront.length - 1; i++) { + double newCrowdingDistance = sortedFront[i].crowdingDistance; + newCrowdingDistance += (sortedFront[i + 1].fitness.get(m) - sortedFront[i - 1].fitness.get(m)) + / (sortedFront[sortedFront.length - 1].fitness.get(m) - sortedFront[0].fitness.get(m)); + + sortedFront[i].crowdingDistance = newCrowdingDistance; + } + } + } + } + } + +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/DepthFirstStrategy.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/DepthFirstStrategy.java new file mode 100644 index 00000000..8192048a --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/DepthFirstStrategy.java @@ -0,0 +1,108 @@ +package tools.refinery.store.query.dse.strategy; + +import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; +import tools.refinery.store.query.dse.Strategy; +import tools.refinery.store.query.dse.internal.Activation; +import tools.refinery.store.query.dse.objectives.Fitness; + +import java.util.Collection; + +public class DepthFirstStrategy implements Strategy { + + private DesignSpaceExplorationAdapter dseAdapter; + + private int maxDepth; + private boolean backTrackIfSolution = true; + + public DepthFirstStrategy() { + this(-1); + } + + public DepthFirstStrategy(int maxDepth) { + if (maxDepth < 0) { + this.maxDepth = Integer.MAX_VALUE; + } else { + this.maxDepth = maxDepth; + } + } + + public DepthFirstStrategy continueIfHardObjectivesFulfilled() { + backTrackIfSolution = false; + return this; + } + + @Override + public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { + this.dseAdapter = designSpaceExplorationAdapter; + } + + @Override + public void explore() { + mainloop: while (true) { + var globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + var isSuccessfulUndo = dseAdapter.backtrack(); + if (!isSuccessfulUndo) { +// "Global constraint is not satisfied and cannot backtrack." + break; + } + else { +// "Global constraint is not satisfied, backtrack." + continue; + } + } + + Fitness fitness = dseAdapter.calculateFitness(); + if (fitness.isSatisfiesHardObjectives()) { + dseAdapter.newSolution(); + if (backTrackIfSolution) { + var isSuccessfulUndo = dseAdapter.backtrack(); + if (!isSuccessfulUndo) { +// "Found a solution but cannot backtrack." + break; + } else { +// "Found a solution, backtrack." + continue; + } + } + } + + var depth = dseAdapter.getDepth(); + if (dseAdapter.getDepth() >= maxDepth) { + var isSuccessfulUndo = dseAdapter.backtrack(); + if (!isSuccessfulUndo) { +// "Reached max depth but cannot backtrack." + break; + } + } + + Collection activations; + do { + activations = dseAdapter.getUntraversedActivations(); + if (activations.isEmpty()) { + if (!dseAdapter.backtrack()) { + // "No more transitions from current state and cannot backtrack." + break mainloop; + } + else { + // "No more transitions from current state, backtrack." + continue; + } + } + } while (activations.isEmpty()); + + dseAdapter.fireRandomActivation(); +// if (dseAdapter.isCurrentInTrajectory()) { +// if (!dseAdapter.backtrack()) { +//// TODO: throw exception +//// "The new state is present in the trajectory but cannot backtrack. Should never happen!" +// break; +// } +// else { +//// "The new state is already visited in the trajectory, backtrack." +// continue; +// } +// } + } + } +} diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java new file mode 100644 index 00000000..06e2e076 --- /dev/null +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java @@ -0,0 +1,117 @@ +package tools.refinery.store.query.dse; + +import guru.nidi.graphviz.engine.Format; +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.dse.internal.TransformationRule; +import tools.refinery.store.query.dse.strategy.BestFirstStrategy; +import tools.refinery.store.query.dse.strategy.DepthFirstStrategy; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.query.view.AnySymbolView; +import tools.refinery.store.query.view.KeyOnlyView; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.ModelVisualizerAdapter; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static tools.refinery.store.query.literal.Literals.not; +import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults; + +public class DesignSpaceExplorationTest { +// private static final Symbol namedElement = Symbol.of("NamedElement", 1); +// private static final Symbol attribute = Symbol.of("Attribute", 1); +// private static final Symbol method = Symbol.of("Method", 1); +// private static final Symbol dataDependency = Symbol.of("DataDependency", 2); +// private static final Symbol functionalDependency = Symbol.of("FunctionalDependency", 2); + + private static final Symbol classModel = Symbol.of("ClassModel", 1); + private static final Symbol classElement = Symbol.of("ClassElement", 1); + private static final Symbol feature = Symbol.of("Feature", 1); + + private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); + private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); + + private static final Symbol features = Symbol.of("Features", 2); + private static final Symbol classes = Symbol.of("Classes", 2); + + private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); + private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); + private static final AnySymbolView featureView = new KeyOnlyView<>(feature); + private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); + private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); + private static final AnySymbolView featuresView = new KeyOnlyView<>(features); + private static final AnySymbolView classesView = new KeyOnlyView<>(classes); + + @Test + void createObjectTest() { + var store = ModelStore.builder() + .with(ViatraModelQueryAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + + assertEquals(0, dseAdapter.getModelSize()); + + var newModel = dseAdapter.createObject(); + var newModelId = newModel.get(0); + var newClass1 = dseAdapter.createObject(); + var newClass1Id = newClass1.get(0); + var newClass2 = dseAdapter.createObject(); + var newClass2Id = newClass2.get(0); + var newField = dseAdapter.createObject(); + var newFieldId = newField.get(0); + + assertEquals(0, newModelId); + assertEquals(1, newClass1Id); + assertEquals(2, newClass2Id); + assertEquals(3, newFieldId); + assertEquals(4, dseAdapter.getModelSize()); + } + + @Test + void deleteMiddleObjectTest() { + var store = ModelStore.builder() + .with(ViatraModelQueryAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + + assertEquals(0, dseAdapter.getModelSize()); + + var newObject0 = dseAdapter.createObject(); + var newObject0Id = newObject0.get(0); + var newObject1 = dseAdapter.createObject(); + var newObject1Id = newObject1.get(0); + var newObject2 = dseAdapter.createObject(); + var newObject2Id = newObject2.get(0); + var newObject3 = dseAdapter.createObject(); + var newObject3Id = newObject3.get(0); + + assertEquals(0, newObject0Id); + assertEquals(1, newObject1Id); + assertEquals(2, newObject2Id); + assertEquals(3, newObject3Id); + assertEquals(4, dseAdapter.getModelSize()); + + dseAdapter.deleteObject(newObject1); + assertEquals(4, dseAdapter.getModelSize()); + + var newObject4 = dseAdapter.createObject(); + var newObject4Id = newObject4.get(0); + assertEquals(4, newObject4Id); + assertEquals(5, dseAdapter.getModelSize()); + + dseAdapter.deleteObject(newObject4); + assertEquals(5, dseAdapter.getModelSize()); + } + +} diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/TransformationRuleTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/TransformationRuleTest.java new file mode 100644 index 00000000..1fb3421b --- /dev/null +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/TransformationRuleTest.java @@ -0,0 +1,403 @@ +package tools.refinery.store.query.dse; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.dse.internal.TransformationRule; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.query.view.AnySymbolView; +import tools.refinery.store.query.view.KeyOnlyView; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static tools.refinery.store.query.literal.Literals.not; +import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults; + +public class TransformationRuleTest { + + private static final Symbol classModel = Symbol.of("ClassModel", 1); + private static final Symbol classElement = Symbol.of("ClassElement", 1); + private static final Symbol feature = Symbol.of("Feature", 1); + + private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); + private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); + + private static final Symbol features = Symbol.of("Features", 2); + private static final Symbol classes = Symbol.of("Classes", 2); + + private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); + private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); + private static final AnySymbolView featureView = new KeyOnlyView<>(feature); + private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); + private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); + private static final AnySymbolView featuresView = new KeyOnlyView<>(features); + private static final AnySymbolView classesView = new KeyOnlyView<>(classes); + + @Test + void activationsTest() { + var assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", + (builder, model, c, f) -> builder.clause( + classElementView.call(c), + classesView.call(model, c), + encapsulatesView.call(c, f) + )); + + var assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f) + -> builder.clause((model, c1) -> List.of( + classModelView.call(model), + featureView.call(f), + classElementView.call(c2), + featuresView.call(model, f), + classesView.call(model, c1), + not(assignFeaturePreconditionHelper.call(model, c2, f)), + not(encapsulatesView.call(c2, f)) + ))); + + var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", + (builder, model, c) -> builder.clause((f) -> List.of( + classModelView.call(model), + classElementView.call(c), + featuresView.call(model, f), + not(encapsulatesView.call(c, f)) + ))); + + TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", + assignFeaturePrecondition, + (model) -> { + var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); + return ((Tuple activation) -> { + var feature = activation.get(0); + var classElement = activation.get(1); + + isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); + }); + }); + + TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + }); + }); + + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(assignFeaturePrecondition, assignFeaturePreconditionHelper, + deleteEmptyClassPrecondition)) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + assignFeatureRule.prepare(model, queryEngine); + deleteEmptyClassRule.prepare(model, queryEngine); + + var classModelInterpretation = model.getInterpretation(classModel); + var classElementInterpretation = model.getInterpretation(classElement); + var featureInterpretation = model.getInterpretation(feature); + var featuresInterpretation = model.getInterpretation(features); + var classesInterpretation = model.getInterpretation(classes); + + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var newModel = dseAdapter.createObject(); + var newModelId = newModel.get(0); + var newClass1 = dseAdapter.createObject(); + var newClass1Id = newClass1.get(0); + var newClass2 = dseAdapter.createObject(); + var newClass2Id = newClass2.get(0); + var newField = dseAdapter.createObject(); + var newFieldId = newField.get(0); + + classModelInterpretation.put(newModel, true); + classElementInterpretation.put(newClass1, true); + classElementInterpretation.put(newClass2, true); + featureInterpretation.put(newField, true); + classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); + classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); + featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); + + queryEngine.flushChanges(); + + var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsSets(); + var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsSets(); + + assertResults(Map.of( + Tuple.of(newClass1Id, newFieldId), true, + Tuple.of(newClass2Id, newFieldId), true + ), assignFeatureRuleActivations); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRuleActivations); + } + + @Test + void randomActivationTest() { + var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", + (builder, model, c) -> builder.clause((f) -> List.of( + classModelView.call(model), + classElementView.call(c), + featuresView.call(model, f), + not(encapsulatesView.call(c, f)) + ))); + + TransformationRule deleteEmptyClassRule0 = new TransformationRule("DeleteEmptyClass0", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + }); + }, + 0L); + + TransformationRule deleteEmptyClassRule1 = new TransformationRule("DeleteEmptyClass1", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + }); + }, + 78634L); + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(deleteEmptyClassPrecondition)) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + deleteEmptyClassRule0.prepare(model, queryEngine); + deleteEmptyClassRule1.prepare(model, queryEngine); + + var classModelInterpretation = model.getInterpretation(classModel); + var classElementInterpretation = model.getInterpretation(classElement); + var featureInterpretation = model.getInterpretation(feature); + var featuresInterpretation = model.getInterpretation(features); + var classesInterpretation = model.getInterpretation(classes); + + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var newModel = dseAdapter.createObject(); + var newModelId = newModel.get(0); + var newClass1 = dseAdapter.createObject(); + var newClass1Id = newClass1.get(0); + var newClass2 = dseAdapter.createObject(); + var newClass2Id = newClass2.get(0); + var newField = dseAdapter.createObject(); + var newFieldId = newField.get(0); + + classModelInterpretation.put(newModel, true); + classElementInterpretation.put(newClass1, true); + classElementInterpretation.put(newClass2, true); + featureInterpretation.put(newField, true); + classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); + classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); + featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); + + queryEngine.flushChanges(); + + + var activation0 = deleteEmptyClassRule0.getRandomActivation().activation(); + var activation1 = deleteEmptyClassRule1.getRandomActivation().activation(); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRule0.getAllActivationsAsSets()); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRule1.getAllActivationsAsSets()); + + assertEquals(Tuple.of(newModelId, newClass2Id), activation0); + assertEquals(Tuple.of(newModelId, newClass1Id), activation1); + + } + + @Test + void fireTest() { + var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", + (builder, model, c) -> builder.clause((f) -> List.of( + classModelView.call(model), + classElementView.call(c), + featuresView.call(model, f), + not(encapsulatesView.call(c, f)) + ))); + + TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(deleteEmptyClassPrecondition)) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + deleteEmptyClassRule.prepare(model, queryEngine); + + var classModelInterpretation = model.getInterpretation(classModel); + var classElementInterpretation = model.getInterpretation(classElement); + var featureInterpretation = model.getInterpretation(feature); + var featuresInterpretation = model.getInterpretation(features); + var classesInterpretation = model.getInterpretation(classes); + + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var newModel = dseAdapter.createObject(); + var newModelId = newModel.get(0); + var newClass1 = dseAdapter.createObject(); + var newClass1Id = newClass1.get(0); + var newClass2 = dseAdapter.createObject(); + var newClass2Id = newClass2.get(0); + var newField = dseAdapter.createObject(); + var newFieldId = newField.get(0); + + classModelInterpretation.put(newModel, true); + classElementInterpretation.put(newClass1, true); + classElementInterpretation.put(newClass2, true); + featureInterpretation.put(newField, true); + classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); + classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); + featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); + + queryEngine.flushChanges(); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRule.getAllActivationsAsSets()); + + + deleteEmptyClassRule.fireActivation(Tuple.of(0, 1)); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), false, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRule.getAllActivationsAsSets()); + } + + @Test + void randomFireTest() { + var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", + (builder, model, c) -> builder.clause((f) -> List.of( + classModelView.call(model), + classElementView.call(c), + featuresView.call(model, f), + not(encapsulatesView.call(c, f)) + ))); + + TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass0", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + }); + }, + 0L); + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(deleteEmptyClassPrecondition)) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + deleteEmptyClassRule.prepare(model, queryEngine); + + var classModelInterpretation = model.getInterpretation(classModel); + var classElementInterpretation = model.getInterpretation(classElement); + var featureInterpretation = model.getInterpretation(feature); + var featuresInterpretation = model.getInterpretation(features); + var classesInterpretation = model.getInterpretation(classes); + + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var newModel = dseAdapter.createObject(); + var newModelId = newModel.get(0); + var newClass1 = dseAdapter.createObject(); + var newClass1Id = newClass1.get(0); + var newClass2 = dseAdapter.createObject(); + var newClass2Id = newClass2.get(0); + var newField = dseAdapter.createObject(); + var newFieldId = newField.get(0); + + classModelInterpretation.put(newModel, true); + classElementInterpretation.put(newClass1, true); + classElementInterpretation.put(newClass2, true); + featureInterpretation.put(newField, true); + classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); + classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); + featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); + + queryEngine.flushChanges(); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRule.getAllActivationsAsSets()); + + deleteEmptyClassRule.fireRandomActivation(); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), false + ), deleteEmptyClassRule.getAllActivationsAsSets()); + + deleteEmptyClassRule.fireRandomActivation(); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), false, + Tuple.of(newModelId, newClass2Id), false + ), deleteEmptyClassRule.getAllActivationsAsSets()); + + } +} 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 aafbe130..24d4088a 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 @@ -59,13 +59,13 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { @Override public ModelStore build() { + for (int i = adapters.size() - 1; i >= 0; i--) { + adapters.get(i).configure(this); + } var stores = new HashMap>(allSymbols.size()); for (var entry : equivalenceClasses.entrySet()) { createStores(stores, entry.getKey(), entry.getValue()); } - for (int i = adapters.size() - 1; i >= 0; i--) { - adapters.get(i).configure(this); - } var modelStore = new ModelStoreImpl(stores, adapters.size()); for (var adapterBuilder : adapters) { var storeAdapter = adapterBuilder.build(modelStore); -- cgit v1.2.3-54-g00ecf From 4f307849e09477cfaa600b50837e999208d05ad6 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Tue, 25 Jul 2023 21:48:55 +0200 Subject: Add best first strategy --- .../query/dse/objectives/TrajectoryFitness.java | 71 ++++++++ .../query/dse/strategy/BestFirstStrategy.java | 189 +++++++++++++++++++++ 2 files changed, 260 insertions(+) create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/TrajectoryFitness.java create mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/BestFirstStrategy.java (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/TrajectoryFitness.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/TrajectoryFitness.java new file mode 100644 index 00000000..b9ff7067 --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/TrajectoryFitness.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.query.dse.objectives; + +import java.util.Arrays; + + +/** + * This class represents a trajectory and its fitness. + * @author Andras Szabolcs Nagy + * + */ +public class TrajectoryFitness { + + public Object[] trajectory; + public Fitness fitness; + + public int rank; + public double crowdingDistance; + + private int hash; + + public int survive; + + /** + * Creates a {@link TrajectoryFitness} with the full trajectory. + * @param trajectory The trajectory. + * @param fitness The fitness. + */ + public TrajectoryFitness(Object[] trajectory, Fitness fitness) { + this.fitness = fitness; + this.trajectory = trajectory; + } + + /** + * Creates a {@link TrajectoryFitness} with the given activation id} + * @param transition The transition. + * @param fitness The fitness. + */ + public TrajectoryFitness(Object transition, Fitness fitness) { + this.fitness = fitness; + trajectory = new Object[] {transition}; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TrajectoryFitness) { + return Arrays.equals(trajectory, ((TrajectoryFitness) obj).trajectory); + } + return false; + } + + @Override + public int hashCode() { + if (hash == 0 && trajectory.length > 0) { + hash = Arrays.hashCode(trajectory); + } + return hash; + } + + @Override + public String toString() { + return Arrays.toString(trajectory) + fitness.toString(); + } +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/BestFirstStrategy.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/BestFirstStrategy.java new file mode 100644 index 00000000..62adb72f --- /dev/null +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/BestFirstStrategy.java @@ -0,0 +1,189 @@ +package tools.refinery.store.query.dse.strategy; + +import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; +import tools.refinery.store.query.dse.Strategy; +import tools.refinery.store.query.dse.internal.Activation; +import tools.refinery.store.query.dse.objectives.Fitness; +import tools.refinery.store.query.dse.objectives.ObjectiveComparatorHelper; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.PriorityQueue; + +public class BestFirstStrategy implements Strategy { + + private DesignSpaceExplorationAdapter dseAdapter; + + private int maxDepth; + private boolean backTrackIfSolution = true; + private boolean onlyBetterFirst = false; + + private PriorityQueue trajectoriesToExplore; + + private static class TrajectoryWithFitness { + + public List trajectory; + public Fitness fitness; + + public TrajectoryWithFitness(List trajectory, Fitness fitness) { + super(); + this.trajectory = trajectory; + this.fitness = fitness; + } + + @Override + public String toString() { + return trajectory.toString() + fitness.toString(); + } + + } + + public BestFirstStrategy() { + this(-1); + } + + public BestFirstStrategy(int maxDepth) { + if (maxDepth < 0) { + this.maxDepth = Integer.MAX_VALUE; + } else { + this.maxDepth = maxDepth; + } + } + + public BestFirstStrategy continueIfHardObjectivesFulfilled() { + backTrackIfSolution = false; + return this; + } + + public BestFirstStrategy goOnOnlyIfFitnessIsBetter() { + onlyBetterFirst = true; + return this; + } + + @Override + public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { + this.dseAdapter = designSpaceExplorationAdapter; + final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); + + trajectoriesToExplore = new PriorityQueue(11, + (o1, o2) -> objectiveComparatorHelper.compare(o2.fitness, o1.fitness)); + } + + @Override + public void explore() { + final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); + + boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + // "Global constraint is not satisfied in the first state. Terminate."); + return; + } + + final Fitness firstFitness = dseAdapter.calculateFitness(); + if (firstFitness.isSatisfiesHardObjectives()) { + dseAdapter.newSolution(); + // "First state is a solution. Terminate."); + if (backTrackIfSolution) { + return; + } + } + + if (maxDepth == 0) { + return; + } + + final List firstTrajectory = dseAdapter.getTrajectory(); + TrajectoryWithFitness currentTrajectoryWithFitness = new TrajectoryWithFitness(firstTrajectory, firstFitness); + trajectoriesToExplore.add(currentTrajectoryWithFitness); + + mainLoop: while (true) { + + if (currentTrajectoryWithFitness == null) { + if (trajectoriesToExplore.isEmpty()) { + // "State space is fully traversed."); + return; + } else { + currentTrajectoryWithFitness = trajectoriesToExplore.element(); +// if (logger.isDebugEnabled()) { +// "New trajectory is chosen: " + currentTrajectoryWithFitness); +// } + dseAdapter.restoreTrajectory(currentTrajectoryWithFitness.trajectory); + } + } + + Collection activations = dseAdapter.getUntraversedActivations(); + Iterator iterator = activations.iterator(); + + + + while (iterator.hasNext()) { + final Activation nextActivation = iterator.next(); + if (!iterator.hasNext()) { + // "Last untraversed activation of the state."); + trajectoriesToExplore.remove(currentTrajectoryWithFitness); + } + +// if (logger.isDebugEnabled()) { +// "Executing new activation: " + nextActivation); +// } + dseAdapter.fireActivation(nextActivation); + if (dseAdapter.isCurrentStateAlreadyTraversed()) { + // "The new state is already visited."); + dseAdapter.backtrack(); + } else if (!dseAdapter.checkGlobalConstraints()) { + // "Global constraint is not satisfied."); + dseAdapter.backtrack(); + } else { + final Fitness nextFitness = dseAdapter.calculateFitness(); + if (nextFitness.isSatisfiesHardObjectives()) { + dseAdapter.newSolution(); + // "Found a solution."); + if (backTrackIfSolution) { + dseAdapter.backtrack(); + continue; + } + } + if (dseAdapter.getDepth() >= maxDepth) { + // "Reached max depth."); + dseAdapter.backtrack(); + continue; + } + + TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness( + dseAdapter.getTrajectory(), nextFitness); + trajectoriesToExplore.add(nextTrajectoryWithFitness); + + int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness, + nextTrajectoryWithFitness.fitness); + if (compare < 0) { + // "Better fitness, moving on: " + nextFitness); + currentTrajectoryWithFitness = nextTrajectoryWithFitness; + continue mainLoop; + } else if (compare == 0) { + if (onlyBetterFirst) { + // "Equally good fitness, backtrack: " + nextFitness); + dseAdapter.backtrack(); + continue; + } else { + // "Equally good fitness, moving on: " + nextFitness); + currentTrajectoryWithFitness = nextTrajectoryWithFitness; + continue mainLoop; + } + } else { + // "Worse fitness."); + currentTrajectoryWithFitness = null; + continue mainLoop; + } + } + } + + // "State is fully traversed."); + trajectoriesToExplore.remove(currentTrajectoryWithFitness); + currentTrajectoryWithFitness = null; + + } + // "Interrupted."); + + } +} -- cgit v1.2.3-54-g00ecf From dd2d8a1517f3f198fe005ebf91a4f8e912609c1b Mon Sep 17 00:00:00 2001 From: nagilooh Date: Tue, 25 Jul 2023 17:17:17 +0200 Subject: Add visualization adapter Uses Graphviz to create a visualization of the models and can save them to disk as an image or various textual representations --- settings.gradle.kts | 1 + .../java/tools/refinery/store/model/Model.java | 4 + .../refinery/store/model/internal/ModelImpl.java | 4 + subprojects/visualization/build.gradle.kts | 14 ++++ .../visualization/ModelVisualizerAdapter.java | 22 ++++++ .../visualization/ModelVisualizerBuilder.java | 6 ++ .../visualization/ModelVisualizerStoreAdapter.java | 7 ++ .../internal/ModelVisualizeStoreAdapterImpl.java | 24 ++++++ .../internal/ModelVisualizerAdapterImpl.java | 88 ++++++++++++++++++++++ .../internal/ModelVisualizerBuilderImpl.java | 14 ++++ 10 files changed, 184 insertions(+) create mode 100644 subprojects/visualization/build.gradle.kts create mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java create mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java create mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java create mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java create mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java create mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java (limited to 'subprojects') diff --git a/settings.gradle.kts b/settings.gradle.kts index 2b1c179d..8b53e7f7 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -17,6 +17,7 @@ include( "store-query", "store-query-viatra", "store-reasoning", + "visualization", ) for (project in rootProject.children) { 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 d58d91c3..703a0720 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 @@ -7,9 +7,11 @@ package tools.refinery.store.model; import tools.refinery.store.adapter.ModelAdapter; import tools.refinery.store.map.Versioned; +import tools.refinery.store.model.internal.VersionedInterpretation; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; +import java.util.Map; import java.util.Optional; public interface Model extends Versioned { @@ -27,6 +29,8 @@ public interface Model extends Versioned { Interpretation getInterpretation(Symbol symbol); + public Map> getInterpretations(); + ModelDiffCursor getDiffCursor(long to); Optional tryGetAdapter(Class adapterType); 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 c5475a1a..bf6987d6 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 @@ -187,4 +187,8 @@ public class ModelImpl implements Model { public void removeListener(ModelListener listener) { listeners.remove(listener); } + + public Map> getInterpretations() { + return interpretations; + } } diff --git a/subprojects/visualization/build.gradle.kts b/subprojects/visualization/build.gradle.kts new file mode 100644 index 00000000..97b1688a --- /dev/null +++ b/subprojects/visualization/build.gradle.kts @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ + +plugins { + id("tools.refinery.gradle.java-library") +} + +dependencies { + implementation ("guru.nidi:graphviz-java:0.18.1") + api(project(":refinery-store-query")) +} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java new file mode 100644 index 00000000..ae23e3bc --- /dev/null +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java @@ -0,0 +1,22 @@ +package tools.refinery.visualization; + +import guru.nidi.graphviz.engine.Format; +import guru.nidi.graphviz.model.MutableGraph; +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl; + +public interface ModelVisualizerAdapter extends ModelAdapter { + + ModelVisualizerStoreAdapter getStoreAdapter(); + static ModelVisualizerBuilder builder() { + return new ModelVisualizerBuilderImpl(); + } + + public MutableGraph createVisualizationForCurrentModelState(); + + public MutableGraph createVisualizationForModelState(Long version); + + public boolean saveVisualization(MutableGraph graph, String path); + + public boolean saveVisualization(MutableGraph graph, Format format, String path); +} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java new file mode 100644 index 00000000..9c1bd0e0 --- /dev/null +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java @@ -0,0 +1,6 @@ +package tools.refinery.visualization; + +import tools.refinery.store.adapter.ModelAdapterBuilder; + +public interface ModelVisualizerBuilder extends ModelAdapterBuilder { +} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java new file mode 100644 index 00000000..764de6d4 --- /dev/null +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java @@ -0,0 +1,7 @@ +package tools.refinery.visualization; + +import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.query.ModelQueryStoreAdapter; + +public interface ModelVisualizerStoreAdapter extends ModelStoreAdapter { +} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java new file mode 100644 index 00000000..6e158c28 --- /dev/null +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java @@ -0,0 +1,24 @@ +package tools.refinery.visualization.internal; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.visualization.ModelVisualizerStoreAdapter; + +public class ModelVisualizeStoreAdapterImpl implements ModelVisualizerStoreAdapter { + private final ModelStore store; + + public ModelVisualizeStoreAdapterImpl(ModelStore store) { + this.store = store; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public ModelAdapter createModelAdapter(Model model) { + return new ModelVisualizerAdapterImpl(model, this); + } +} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java new file mode 100644 index 00000000..475ae416 --- /dev/null +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -0,0 +1,88 @@ +package tools.refinery.visualization.internal; + +import guru.nidi.graphviz.attribute.Label; +import guru.nidi.graphviz.engine.Format; +import guru.nidi.graphviz.engine.Graphviz; +import guru.nidi.graphviz.model.MutableGraph; +import tools.refinery.store.model.Model; +import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.ModelVisualizerStoreAdapter; + +import java.io.File; +import java.io.IOException; + +import static guru.nidi.graphviz.model.Factory.*; + +public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { + private final Model model; + private final ModelVisualizerStoreAdapter storeAdapter; + public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { + this.model = model; + this.storeAdapter = storeAdapter; + } + + @Override + public Model getModel() { + return model; + } + + @Override + public ModelVisualizerStoreAdapter getStoreAdapter() { + return storeAdapter; + } + + @Override + public MutableGraph createVisualizationForCurrentModelState() { + var interpretations = model.getInterpretations(); + MutableGraph graph = mutGraph("model").setDirected(true); + for (var entry : interpretations.entrySet()) { + var key = entry.getKey(); + var arity = key.arity(); + if (arity < 1 || arity > 2) { + continue; + } + var valueType = key.valueType(); + // TODO: support TruthValue + if (valueType != Boolean.class) { + continue; + } + var cursor = entry.getValue().getAll(); + while (cursor.move()) { + if (arity == 1) { + var id = cursor.getKey().get(0); + graph.add(mutNode(String.valueOf(id)).add("label", key.name() + ": " + id)); + } else { + var from = cursor.getKey().get(0); + var to = cursor.getKey().get(1); + graph.add(mutNode(String.valueOf(from)).addLink(to(mutNode(String.valueOf(to))).with(Label.of(key.name())))); + } + } + } + return graph; + } + + @Override + public MutableGraph createVisualizationForModelState(Long version) { + var currentVersion = model.getState(); + model.restore(version); + MutableGraph graph = createVisualizationForCurrentModelState(); + model.restore(currentVersion); + return graph; + } + + @Override + public boolean saveVisualization(MutableGraph graph, String path) { + return saveVisualization(graph, Format.PNG, path); + } + + @Override + public boolean saveVisualization(MutableGraph graph, Format format, String path) { + try { + Graphviz.fromGraph(graph).render(format).toFile(new File(path)); + return true; + } catch (IOException e) { + e.printStackTrace(); + return false; + } + } +} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java new file mode 100644 index 00000000..4148c24a --- /dev/null +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java @@ -0,0 +1,14 @@ +package tools.refinery.visualization.internal; + +import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.model.ModelStore; +import tools.refinery.visualization.ModelVisualizerBuilder; + +public class ModelVisualizerBuilderImpl + extends AbstractModelAdapterBuilder + implements ModelVisualizerBuilder { + @Override + protected ModelVisualizeStoreAdapterImpl doBuild(ModelStore store) { + return new ModelVisualizeStoreAdapterImpl(store); + } +} -- cgit v1.2.3-54-g00ecf From 7dfa50ea739ce76b650fd3f957ae74619af69b85 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Wed, 26 Jul 2023 12:04:09 +0200 Subject: Fix issue with storing trajectory --- .../store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java index 5adf8306..eb2fc063 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -42,7 +42,7 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration private boolean isNewState = false; public List getTrajectory() { - return trajectory; + return new LinkedList<>(trajectory); } public DesignSpaceExplorationAdapterImpl(Model model, DesignSpaceExplorationStoreAdapterImpl storeAdapter) { -- cgit v1.2.3-54-g00ecf From c272a44efcff7d35a6ba31ef3cd12d1eb17640a0 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Wed, 26 Jul 2023 17:48:25 +0200 Subject: Versioned.commit + Versioned.restore uses Version instead of long. When a Version is collected by gc, the store lets the state get collected by gc as well. --- .../java/tools/refinery/store/map/Version.java | 26 +++++++++ .../java/tools/refinery/store/map/Versioned.java | 18 +++++- .../tools/refinery/store/map/VersionedMap.java | 2 +- .../refinery/store/map/VersionedMapStore.java | 8 +-- .../store/map/VersionedMapStoreFactoryBuilder.java | 1 + .../VersionedMapStoreFactoryBuilderImpl.java | 23 ++++++-- .../store/map/internal/delta/MapTransaction.java | 10 ++-- .../map/internal/delta/VersionedMapDeltaImpl.java | 34 ++++++++--- .../internal/delta/VersionedMapStoreDeltaImpl.java | 41 ++++++-------- .../store/map/internal/state/ImmutableNode.java | 3 +- .../state/StateBasedVersionedMapStoreFactory.java | 11 +++- .../map/internal/state/VersionedMapStateImpl.java | 6 +- .../state/VersionedMapStoreStateConfiguration.java | 8 ++- .../internal/state/VersionedMapStoreStateImpl.java | 44 ++++++--------- .../tools/refinery/store/model/Interpretation.java | 3 +- .../java/tools/refinery/store/model/Model.java | 8 +-- .../tools/refinery/store/model/ModelListener.java | 4 +- .../tools/refinery/store/model/ModelStore.java | 8 +-- .../refinery/store/model/internal/ModelImpl.java | 66 +++++++++++----------- .../model/internal/ModelStoreBuilderImpl.java | 6 +- .../store/model/internal/ModelStoreImpl.java | 37 ++++++------ .../store/model/internal/ModelVersion.java | 39 +++++++++++++ .../model/internal/VersionedInterpretation.java | 14 ++--- .../store/map/tests/fuzz/DiffCursorFuzzTest.java | 21 +++---- .../map/tests/fuzz/MultiThreadTestRunnable.java | 16 +++--- .../store/map/tests/fuzz/RestoreFuzzTest.java | 5 +- .../store/map/tests/fuzz/SharedStoreFuzzTest.java | 5 +- .../map/tests/fuzz/utils/FuzzTestCollections.java | 13 ++++- .../store/map/tests/utils/MapTestEnvironment.java | 2 +- .../refinery/store/model/tests/ModelTest.java | 5 +- 30 files changed, 298 insertions(+), 189 deletions(-) create mode 100644 subprojects/store/src/main/java/tools/refinery/store/map/Version.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/Version.java b/subprojects/store/src/main/java/tools/refinery/store/map/Version.java new file mode 100644 index 00000000..fa2734e4 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/map/Version.java @@ -0,0 +1,26 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.map; + +/** + * Interface denoting versions of {@link Versioned}. + */ +public interface Version { + /** + * Hashcode should be updated in accordance with equals. + * @return a hashcode of the object. + */ + int hashCode(); + + /** + * Equivalence of two {@link Version}. This equivalence must satisfy the following constraint (in addition to the + * constraints of {@link Object#equals(Object)}: if {@code v1} and {@code v2} are {@link Version}s, and {@code v1 + * .equals(v2)}, then {@code versioned.restore(v1)} must be {@code equals} to {@code versioned.restore(v2)}. + * @param o the other object. + * @return weather the two versions are equals. + */ + boolean equals(Object o); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java b/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java index 55720db3..da12b0a9 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/Versioned.java @@ -5,8 +5,20 @@ */ package tools.refinery.store.map; +/** + * Object that can save and restore its state. + */ public interface Versioned { - public long commit(); - //maybe revert()? - public void restore(long state); + /** + * Saves the state of the object. + * @return an object that marks the version of the object at the time the function was called. + */ + Version commit(); + + /** + * Restores the state of the object. + * @param state a {@link Version} object that marks the version. The state must be a {@link Version} object + * returned by a previous {@link #commit()}! + */ + void restore(Version state); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java index c8226c3e..28194b58 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMap.java @@ -16,5 +16,5 @@ public non-sealed interface VersionedMap extends AnyVersionedMap { void putAll(Cursor cursor); - DiffCursor getDiffCursor(long state); + DiffCursor getDiffCursor(Version state); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java index b24c404c..55cf08a5 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStore.java @@ -7,17 +7,13 @@ package tools.refinery.store.map; import tools.refinery.store.map.internal.VersionedMapStoreFactoryBuilderImpl; -import java.util.Set; - public interface VersionedMapStore { VersionedMap createMap(); - VersionedMap createMap(long state); - - Set getStates(); + VersionedMap createMap(Version state); - DiffCursor getDiffCursor(long fromState, long toState); + DiffCursor getDiffCursor(Version fromState, Version toState); static VersionedMapStoreFactoryBuilder builder() { return new VersionedMapStoreFactoryBuilderImpl<>(); diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java index 6b4fc2a0..0ac196f2 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/VersionedMapStoreFactoryBuilder.java @@ -20,6 +20,7 @@ public interface VersionedMapStoreFactoryBuilder { VersionedMapStoreFactoryBuilder defaultValue(V defaultValue); VersionedMapStoreFactoryBuilder strategy(StoreStrategy strategy); + VersionedMapStoreFactoryBuilder versionFreeing(boolean enabled); VersionedMapStoreFactoryBuilder stateBasedImmutableWhenCommitting(boolean transformToImmutable); VersionedMapStoreFactoryBuilder stateBasedSharingStrategy(SharingStrategy sharingStrategy); VersionedMapStoreFactoryBuilder stateBasedHashProvider(ContinuousHashProvider hashProvider); diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java index 47470236..9f419ce1 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/VersionedMapStoreFactoryBuilderImpl.java @@ -18,6 +18,7 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt private StoreStrategy strategy = null; private Boolean transformToImmutable = null; private SharingStrategy sharingStrategy = null; + private Boolean enableVersionFreeing = null; private ContinuousHashProvider continuousHashProvider = null; private DeltaTransactionStrategy deltaTransactionStrategy = null; @@ -64,6 +65,13 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt return this; } + @Override + public VersionedMapStoreFactoryBuilder versionFreeing(boolean enabled) { + this.enableVersionFreeing = enabled; + checkStrategy(); + return this; + } + @Override public VersionedMapStoreFactoryBuilder stateBasedImmutableWhenCommitting(boolean transformToImmutable) { this.transformToImmutable = transformToImmutable; @@ -118,6 +126,7 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt yield new StateBasedVersionedMapStoreFactory<>(defaultValue, getOrDefault(transformToImmutable,true), getOrDefault(sharingStrategy, SharingStrategy.SHARED_NODE_CACHE_IN_GROUP), + getOrDefault(enableVersionFreeing, true), continuousHashProvider); } case DELTA -> new DeltaBasedVersionedMapStoreFactory<>(defaultValue, @@ -127,13 +136,15 @@ public class VersionedMapStoreFactoryBuilderImpl implements VersionedMapSt @Override public String toString() { - return "VersionedMapStoreBuilder{" + - "defaultValue=" + defaultValue + + return "VersionedMapStoreFactoryBuilderImpl{" + + "defaultSet=" + defaultSet + + ", defaultValue=" + defaultValue + ", strategy=" + strategy + - ", stateBasedImmutableWhenCommitting=" + transformToImmutable + - ", stateBasedNodeSharingStrategy=" + sharingStrategy + - ", hashProvider=" + continuousHashProvider + - ", deltaStorageStrategy=" + deltaTransactionStrategy + + ", transformToImmutable=" + transformToImmutable + + ", sharingStrategy=" + sharingStrategy + + ", enableVersionFreeing=" + enableVersionFreeing + + ", continuousHashProvider=" + continuousHashProvider + + ", deltaTransactionStrategy=" + deltaTransactionStrategy + '}'; } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java index 7f9ccd7f..6f3fa6d7 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/MapTransaction.java @@ -5,17 +5,19 @@ */ package tools.refinery.store.map.internal.delta; +import tools.refinery.store.map.Version; + import java.util.Arrays; import java.util.Objects; -public record MapTransaction(MapDelta[] deltas, long version, MapTransaction parent) { +public record MapTransaction(MapDelta[] deltas, MapTransaction parent, int depth) implements Version { @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + Arrays.hashCode(deltas); - result = prime * result + Objects.hash(parent, version); + result = prime * result + Objects.hash(parent, depth); return result; } @@ -29,11 +31,11 @@ public record MapTransaction(MapDelta[] deltas, long version, MapTra return false; @SuppressWarnings("unchecked") MapTransaction other = (MapTransaction) obj; - return Arrays.equals(deltas, other.deltas) && Objects.equals(parent, other.parent) && version == other.version; + return depth == other.depth && Objects.equals(parent, other.parent) && Arrays.equals(deltas, other.deltas); } @Override public String toString() { - return "MapTransaction " + version + " " + Arrays.toString(deltas); + return "MapTransaction " + depth + " " + Arrays.toString(deltas); } } 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 5bb864ac..c19cc817 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 @@ -38,15 +38,15 @@ public class VersionedMapDeltaImpl implements VersionedMap { } @Override - public long commit() { + public Version commit() { MapDelta[] deltas = uncommittedStore.extractAndDeleteDeltas(); - long[] versionContainer = new long[1]; - this.previous = this.store.appendTransaction(deltas, previous, versionContainer); - return versionContainer[0]; + final MapTransaction committedTransaction = this.store.appendTransaction(deltas, previous); + this.previous = committedTransaction; + return committedTransaction; } @Override - public void restore(long state) { + public void restore(Version state) { // 1. restore uncommitted states MapDelta[] uncommitted = this.uncommittedStore.extractAndDeleteDeltas(); if (uncommitted != null) { @@ -61,7 +61,7 @@ public class VersionedMapDeltaImpl implements VersionedMap { this.forward(forward); } else { List[]> backward = new ArrayList<>(); - parent = this.store.getPath(this.previous.version(), state, backward, forward); + parent = this.store.getPath(this.previous, state, backward, forward); this.backward(backward); this.forward(forward); } @@ -75,12 +75,16 @@ public class VersionedMapDeltaImpl implements VersionedMap { } protected void backward(List[]> changes) { + //Currently, this loop statement is faster. + //noinspection ForLoopReplaceableByForEach for (int i = 0; i < changes.size(); i++) { backward(changes.get(i)); } } protected void forward(MapDelta[] changes) { + //Currently, this loop statement is faster. + //noinspection ForLoopReplaceableByForEach for (int i = 0; i < changes.length; i++) { final MapDelta change = changes[i]; K key = change.getKey(); @@ -168,7 +172,7 @@ public class VersionedMapDeltaImpl implements VersionedMap { } @Override - public DiffCursor getDiffCursor(long state) { + public DiffCursor getDiffCursor(Version state) { MapDelta[] backward = this.uncommittedStore.extractDeltas(); List[]> backwardTransactions = new ArrayList<>(); List[]> forwardTransactions = new ArrayList<>(); @@ -178,7 +182,7 @@ public class VersionedMapDeltaImpl implements VersionedMap { } if (this.previous != null) { - store.getPath(this.previous.version(), state, backwardTransactions, forwardTransactions); + store.getPath(this.previous, state, backwardTransactions, forwardTransactions); } else { store.getPath(state, forwardTransactions); } @@ -216,5 +220,19 @@ public class VersionedMapDeltaImpl implements VersionedMap { throw new IllegalStateException("null value stored in map!"); } } + MapTransaction transaction = this.previous; + while(transaction != null) { + MapTransaction parent = transaction.parent(); + if(parent != null) { + if(parent.depth() != transaction.depth()-1) { + throw new IllegalStateException("Parent depths are inconsistent!"); + } + } else { + if(transaction.depth() != 0) { + throw new IllegalArgumentException("Root depth is not 0!"); + } + } + transaction = transaction.parent(); + } } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java index 7f56ea77..ed169409 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/delta/VersionedMapStoreDeltaImpl.java @@ -6,6 +6,7 @@ package tools.refinery.store.map.internal.delta; import tools.refinery.store.map.DiffCursor; +import tools.refinery.store.map.Version; import tools.refinery.store.map.VersionedMap; import tools.refinery.store.map.VersionedMapStore; @@ -18,10 +19,6 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore // Static data protected final V defaultValue; - // Dynamic data - protected final Map> states = new HashMap<>(); - protected long nextID = 0; - public VersionedMapStoreDeltaImpl(boolean summarizeChanges, V defaultValue) { this.summarizeChanges = summarizeChanges; this.defaultValue = defaultValue; @@ -33,30 +30,32 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore } @Override - public VersionedMap createMap(long state) { + public VersionedMap createMap(Version state) { VersionedMapDeltaImpl result = new VersionedMapDeltaImpl<>(this, this.summarizeChanges, this.defaultValue); result.restore(state); return result; } - public synchronized MapTransaction appendTransaction(MapDelta[] deltas, MapTransaction previous, long[] versionContainer) { - long version = nextID++; - versionContainer[0] = version; + public MapTransaction appendTransaction(MapDelta[] deltas, MapTransaction previous) { if (deltas == null) { - states.put(version, previous); return previous; } else { - MapTransaction transaction = new MapTransaction<>(deltas, version, previous); - states.put(version, transaction); - return transaction; + final int depth; + if(previous != null) { + depth = previous.depth()+1; + } else { + depth = 0; + } + return new MapTransaction<>(deltas, previous, depth); } } - private synchronized MapTransaction getState(long state) { - return states.get(state); + @SuppressWarnings("unchecked") + private MapTransaction getState(Version state) { + return (MapTransaction) state; } - public MapTransaction getPath(long to, List[]> forwardTransactions) { + public MapTransaction getPath(Version to, List[]> forwardTransactions) { final MapTransaction target = getState(to); MapTransaction toTransaction = target; while (toTransaction != null) { @@ -66,7 +65,7 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore return target; } - public MapTransaction getPath(long from, long to, + public MapTransaction getPath(Version from, Version to, List[]> backwardTransactions, List[]> forwardTransactions) { MapTransaction fromTransaction = getState(from); @@ -74,7 +73,7 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore MapTransaction toTransaction = target; while (fromTransaction != toTransaction) { - if (fromTransaction == null || (toTransaction != null && fromTransaction.version() < toTransaction.version())) { + if (fromTransaction == null || (toTransaction != null && fromTransaction.depth() < toTransaction.depth())) { forwardTransactions.add(toTransaction.deltas()); toTransaction = toTransaction.parent(); } else { @@ -85,14 +84,8 @@ public class VersionedMapStoreDeltaImpl implements VersionedMapStore return target; } - - @Override - public synchronized Set getStates() { - return new HashSet<>(states.keySet()); - } - @Override - public DiffCursor getDiffCursor(long fromState, long toState) { + public DiffCursor getDiffCursor(Version fromState, Version toState) { List[]> backwardTransactions = new ArrayList<>(); List[]> forwardTransactions = new ArrayList<>(); getPath(fromState, toState, backwardTransactions, forwardTransactions); diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java index 722f9ed7..5b1d8b77 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/ImmutableNode.java @@ -9,8 +9,9 @@ import java.util.Arrays; import java.util.Map; import tools.refinery.store.map.ContinuousHashProvider; +import tools.refinery.store.map.Version; -public class ImmutableNode extends Node { +public class ImmutableNode extends Node implements Version { /** * Bitmap defining the stored key and values. */ diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java index 9409ace0..ccc791a8 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/StateBasedVersionedMapStoreFactory.java @@ -14,14 +14,19 @@ public class StateBasedVersionedMapStoreFactory implements VersionedMapSto private final ContinuousHashProvider continuousHashProvider; private final VersionedMapStoreStateConfiguration config; - public StateBasedVersionedMapStoreFactory(V defaultValue, Boolean transformToImmutable, VersionedMapStoreFactoryBuilder.SharingStrategy sharingStrategy, ContinuousHashProvider continuousHashProvider) { + public StateBasedVersionedMapStoreFactory(V defaultValue, Boolean transformToImmutable, + VersionedMapStoreFactoryBuilder.SharingStrategy sharingStrategy, + boolean versionFreeingEnabled, + ContinuousHashProvider continuousHashProvider) { this.defaultValue = defaultValue; this.continuousHashProvider = continuousHashProvider; this.config = new VersionedMapStoreStateConfiguration( transformToImmutable, - sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE || sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP, - sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP); + sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE + || sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP, + sharingStrategy == VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE_IN_GROUP, + versionFreeingEnabled); } @Override diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java index 817fc70b..57eeccf6 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStateImpl.java @@ -115,7 +115,7 @@ public class VersionedMapStateImpl implements VersionedMap { } @Override - public DiffCursor getDiffCursor(long toVersion) { + public DiffCursor getDiffCursor(Version toVersion) { InOrderMapCursor fromCursor = new InOrderMapCursor<>(this); VersionedMapStateImpl toMap = (VersionedMapStateImpl) this.store.createMap(toVersion); InOrderMapCursor toCursor = new InOrderMapCursor<>(toMap); @@ -124,7 +124,7 @@ public class VersionedMapStateImpl implements VersionedMap { @Override - public long commit() { + public Version commit() { return this.store.commit(root, this); } @@ -133,7 +133,7 @@ public class VersionedMapStateImpl implements VersionedMap { } @Override - public void restore(long state) { + public void restore(Version state) { root = this.store.revert(state); } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java index 45f30cc7..6650f565 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateConfiguration.java @@ -14,11 +14,12 @@ public class VersionedMapStoreStateConfiguration { } public VersionedMapStoreStateConfiguration(boolean immutableWhenCommitting, boolean sharedNodeCacheInStore, - boolean sharedNodeCacheInStoreGroups) { + boolean sharedNodeCacheInStoreGroups, boolean versionFreeingEnabled) { super(); this.immutableWhenCommitting = immutableWhenCommitting; this.sharedNodeCacheInStore = sharedNodeCacheInStore; this.sharedNodeCacheInStoreGroups = sharedNodeCacheInStoreGroups; + this.versionFreeingEnabled = versionFreeingEnabled; } /** @@ -53,4 +54,9 @@ public class VersionedMapStoreStateConfiguration { public boolean isSharedNodeCacheInStoreGroups() { return sharedNodeCacheInStoreGroups; } + + private boolean versionFreeingEnabled = true; + public boolean isVersionFreeingEnabled() { + return versionFreeingEnabled; + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java index 0651772a..8ff3f8e7 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/map/internal/state/VersionedMapStoreStateImpl.java @@ -17,10 +17,7 @@ public class VersionedMapStoreStateImpl implements VersionedMapStore protected final ContinuousHashProvider hashProvider; protected final V defaultValue; - // Dynamic data - protected final Map> states = new HashMap<>(); protected final Map, ImmutableNode> nodeCache; - protected long nextID = 0; public VersionedMapStoreStateImpl(ContinuousHashProvider hashProvider, V defaultValue, VersionedMapStoreStateConfiguration config) { @@ -28,7 +25,7 @@ public class VersionedMapStoreStateImpl implements VersionedMapStore this.hashProvider = hashProvider; this.defaultValue = defaultValue; if (config.isSharedNodeCacheInStore()) { - nodeCache = new HashMap<>(); + nodeCache = createNoteCache(config); } else { nodeCache = null; } @@ -53,7 +50,7 @@ public class VersionedMapStoreStateImpl implements VersionedMapStore if (config.isSharedNodeCacheInStoreGroups()) { Map, ImmutableNode> nodeCache; if (config.isSharedNodeCacheInStore()) { - nodeCache = new HashMap<>(); + nodeCache = createNoteCache(config); } else { nodeCache = null; } @@ -68,39 +65,36 @@ public class VersionedMapStoreStateImpl implements VersionedMapStore return result; } + private static Map createNoteCache(VersionedMapStoreStateConfiguration config) { + if(config.isVersionFreeingEnabled()) { + return new WeakHashMap<>(); + } else { + return new HashMap<>(); + } + } + public static List> createSharedVersionedMapStores(int amount, ContinuousHashProvider hashProvider, V defaultValue) { return createSharedVersionedMapStores(amount, hashProvider, defaultValue, new VersionedMapStoreStateConfiguration()); } - @Override - public synchronized Set getStates() { - return new HashSet<>(states.keySet()); - } - @Override public VersionedMap createMap() { return new VersionedMapStateImpl<>(this, hashProvider, defaultValue); } @Override - public VersionedMap createMap(long state) { + public VersionedMap createMap(Version state) { ImmutableNode data = revert(state); return new VersionedMapStateImpl<>(this, hashProvider, defaultValue, data); } - public synchronized ImmutableNode revert(long state) { - if (states.containsKey(state)) { - return states.get(state); - } else { - ArrayList existingKeys = new ArrayList<>(states.keySet()); - Collections.sort(existingKeys); - throw new IllegalArgumentException("Store does not contain state " + state + "! Available states: " - + Arrays.toString(existingKeys.toArray())); - } + @SuppressWarnings("unchecked") + public synchronized ImmutableNode revert(Version state) { + return (ImmutableNode) state; } - public synchronized long commit(Node data, VersionedMapStateImpl mapToUpdateRoot) { + public synchronized Version commit(Node data, VersionedMapStateImpl mapToUpdateRoot) { ImmutableNode immutable; if (data != null) { immutable = data.toImmutable(this.nodeCache); @@ -108,18 +102,14 @@ public class VersionedMapStoreStateImpl implements VersionedMapStore immutable = null; } - if (nextID == Long.MAX_VALUE) - throw new IllegalStateException("Map store run out of Id-s"); - long id = nextID++; - this.states.put(id, immutable); if (this.immutableWhenCommitting) { mapToUpdateRoot.setRoot(immutable); } - return id; + return immutable; } @Override - public DiffCursor getDiffCursor(long fromState, long toState) { + public DiffCursor getDiffCursor(Version fromState, Version toState) { VersionedMapStateImpl map1 = (VersionedMapStateImpl) createMap(fromState); VersionedMapStateImpl map2 = (VersionedMapStateImpl) createMap(toState); InOrderMapCursor cursor1 = new InOrderMapCursor<>(map1); 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 26ad9a69..72f188d3 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 @@ -7,6 +7,7 @@ package tools.refinery.store.model; import tools.refinery.store.map.Cursor; import tools.refinery.store.map.DiffCursor; +import tools.refinery.store.map.Version; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; @@ -22,7 +23,7 @@ public non-sealed interface Interpretation extends AnyInterpretation { void putAll(Cursor cursor); - DiffCursor getDiffCursor(long to); + DiffCursor getDiffCursor(Version to); void addListener(InterpretationListener listener, boolean alsoWhenRestoring); 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 d58d91c3..a028b81b 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 @@ -6,6 +6,7 @@ package tools.refinery.store.model; import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.map.Version; import tools.refinery.store.map.Versioned; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; @@ -13,11 +14,10 @@ import tools.refinery.store.representation.Symbol; import java.util.Optional; public interface Model extends Versioned { - long NO_STATE_ID = -1; - + Version NO_STATE_ID = null; ModelStore getStore(); - long getState(); + Version getState(); boolean hasUncommittedChanges(); @@ -27,7 +27,7 @@ public interface Model extends Versioned { Interpretation getInterpretation(Symbol symbol); - ModelDiffCursor getDiffCursor(long to); + ModelDiffCursor getDiffCursor(Version to); Optional tryGetAdapter(Class adapterType); diff --git a/subprojects/store/src/main/java/tools/refinery/store/model/ModelListener.java b/subprojects/store/src/main/java/tools/refinery/store/model/ModelListener.java index a9ad8cfd..703ee10a 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/model/ModelListener.java +++ b/subprojects/store/src/main/java/tools/refinery/store/model/ModelListener.java @@ -5,6 +5,8 @@ */ package tools.refinery.store.model; +import tools.refinery.store.map.Version; + public interface ModelListener { default void beforeCommit() { } @@ -12,7 +14,7 @@ public interface ModelListener { default void afterCommit() { } - default void beforeRestore(long state) { + default void beforeRestore(Version state) { } default void afterRestore() { 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 b10eb8a4..89382b3a 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 @@ -6,23 +6,21 @@ package tools.refinery.store.model; import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.map.Version; import tools.refinery.store.model.internal.ModelStoreBuilderImpl; import tools.refinery.store.representation.AnySymbol; import java.util.Collection; import java.util.Optional; -import java.util.Set; public interface ModelStore { Collection getSymbols(); Model createEmptyModel(); - Model createModelForState(long state); + Model createModelForState(Version state); - Set getStates(); - - ModelDiffCursor getDiffCursor(long from, long to); + ModelDiffCursor getDiffCursor(Version from, Version to); Optional tryGetAdapter(Class adapterType); 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 c5475a1a..c2ad9257 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 @@ -8,6 +8,7 @@ package tools.refinery.store.model.internal; import tools.refinery.store.adapter.AdapterUtils; import tools.refinery.store.adapter.ModelAdapter; import tools.refinery.store.map.DiffCursor; +import tools.refinery.store.map.Version; import tools.refinery.store.model.*; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; @@ -17,21 +18,21 @@ import java.util.*; public class ModelImpl implements Model { private final ModelStore store; - private long state; - private Map> interpretations; + private Version state; + private LinkedHashMap> interpretations; private final List adapters; private final List listeners = new ArrayList<>(); private boolean uncommittedChanges; private ModelAction pendingAction = ModelAction.NONE; - private long restoringToState = NO_STATE_ID; + private Version restoringToState = null; - ModelImpl(ModelStore store, long state, int adapterCount) { + ModelImpl(ModelStore store, Version state, int adapterCount) { this.store = store; this.state = state; adapters = new ArrayList<>(adapterCount); } - void setInterpretations(Map> interpretations) { + void setInterpretations(LinkedHashMap> interpretations) { this.interpretations = interpretations; } @@ -41,7 +42,7 @@ public class ModelImpl implements Model { } @Override - public long getState() { + public Version getState() { return state; } @@ -57,7 +58,7 @@ public class ModelImpl implements Model { } @Override - public ModelDiffCursor getDiffCursor(long to) { + public ModelDiffCursor getDiffCursor(Version to) { var diffCursors = new HashMap>(interpretations.size()); for (var entry : interpretations.entrySet()) { diffCursors.put(entry.getKey(), entry.getValue().getDiffCursor(to)); @@ -65,7 +66,7 @@ public class ModelImpl implements Model { return new ModelDiffCursor(diffCursors); } - private void setState(long state) { + private void setState(Version state) { this.state = state; uncommittedChanges = false; } @@ -82,11 +83,11 @@ public class ModelImpl implements Model { } private boolean hasPendingAction() { - return pendingAction != ModelAction.NONE || restoringToState != NO_STATE_ID; + return pendingAction != ModelAction.NONE || restoringToState != null; } @Override - public long commit() { + public Version commit() { if (hasPendingAction()) { throw pendingActionError("commit"); } @@ -94,43 +95,40 @@ public class ModelImpl implements Model { try { int listenerCount = listeners.size(); int i = listenerCount; - long version = 0; + + // Before commit message to listeners while (i > 0) { i--; listeners.get(i).beforeCommit(); } - boolean versionSet = false; - for (var interpretation : interpretations.values()) { - long newVersion = interpretation.commit(); - if (versionSet) { - if (version != newVersion) { - throw new IllegalStateException("Interpretations in model have different versions (%d and %d)" - .formatted(version, newVersion)); - } - } else { - version = newVersion; - versionSet = true; - } + + // Doing the commit on the interpretations + Version[] interpretationVersions = new Version[interpretations.size()]; + int j = 0; + for(var interpretationEntry : interpretations.entrySet()) { + interpretationVersions[j++] = interpretationEntry.getValue().commit(); } - setState(version); + ModelVersion modelVersion = new ModelVersion(interpretationVersions); + setState(modelVersion); + + // After commit message to listeners while (i < listenerCount) { listeners.get(i).afterCommit(); i++; } - return version; + + return modelVersion; } finally { pendingAction = ModelAction.NONE; } } @Override - public void restore(long version) { + public void restore(Version version) { if (hasPendingAction()) { - throw pendingActionError("restore to %d".formatted(version)); - } - if (!store.getStates().contains(version)) { - throw new IllegalArgumentException("Store does not contain state %d".formatted(version)); + throw pendingActionError("restore to %s".formatted(version)); } + pendingAction = ModelAction.RESTORE; restoringToState = version; try { @@ -140,9 +138,11 @@ public class ModelImpl implements Model { i--; listeners.get(i).beforeRestore(version); } + int j = 0; for (var interpretation : interpretations.values()) { - interpretation.restore(version); + interpretation.restore(ModelVersion.getInternalVersion(version,j++)); } + setState(version); while (i < listenerCount) { listeners.get(i).afterRestore(); @@ -150,7 +150,7 @@ public class ModelImpl implements Model { } } finally { pendingAction = ModelAction.NONE; - restoringToState = NO_STATE_ID; + restoringToState = null; } } @@ -159,7 +159,7 @@ public class ModelImpl implements Model { case NONE -> throw new IllegalArgumentException("Trying to throw pending action error when there is no " + "pending action"); case COMMIT -> "commit"; - case RESTORE -> "restore to %d".formatted(restoringToState); + case RESTORE -> "restore to %s".formatted(restoringToState); }; return new IllegalStateException("Cannot %s due to pending %s".formatted(currentActionName, pendingActionName)); } 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 2cc7b19c..65fa8d24 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 @@ -19,8 +19,8 @@ import tools.refinery.store.tuple.Tuple; import java.util.*; public class ModelStoreBuilderImpl implements ModelStoreBuilder { - private final Set allSymbols = new HashSet<>(); - private final Map, List> equivalenceClasses = new HashMap<>(); + private final LinkedHashSet allSymbols = new LinkedHashSet<>(); + private final LinkedHashMap, List> equivalenceClasses = new LinkedHashMap<>(); private final List adapters = new ArrayList<>(); @Override @@ -59,7 +59,7 @@ public class ModelStoreBuilderImpl implements ModelStoreBuilder { @Override public ModelStore build() { - var stores = new HashMap>(allSymbols.size()); + var stores = new LinkedHashMap>(allSymbols.size()); for (var entry : equivalenceClasses.entrySet()) { createStores(stores, entry.getKey(), entry.getValue()); } 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 60b735e6..a320a618 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 @@ -8,8 +8,8 @@ package tools.refinery.store.model.internal; import tools.refinery.store.adapter.AdapterUtils; import tools.refinery.store.adapter.ModelStoreAdapter; import tools.refinery.store.map.DiffCursor; +import tools.refinery.store.map.Version; import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelDiffCursor; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.AnySymbol; @@ -18,10 +18,10 @@ import tools.refinery.store.tuple.Tuple; import java.util.*; public class ModelStoreImpl implements ModelStore { - private final Map> stores; + private final LinkedHashMap> stores; private final List adapters; - ModelStoreImpl(Map> stores, int adapterCount) { + ModelStoreImpl(LinkedHashMap> stores, int adapterCount) { this.stores = stores; adapters = new ArrayList<>(adapterCount); } @@ -31,14 +31,14 @@ public class ModelStoreImpl implements ModelStore { return Collections.unmodifiableCollection(stores.keySet()); } - private ModelImpl createModelWithoutInterpretations(long state) { + private ModelImpl createModelWithoutInterpretations(Version state) { return new ModelImpl(this, state, adapters.size()); } @Override public ModelImpl createEmptyModel() { - var model = createModelWithoutInterpretations(Model.NO_STATE_ID); - var interpretations = new HashMap>(stores.size()); + var model = createModelWithoutInterpretations(null); + var interpretations = new LinkedHashMap>(stores.size()); for (var entry : this.stores.entrySet()) { var symbol = entry.getKey(); interpretations.put(symbol, VersionedInterpretation.of(model, symbol, entry.getValue())); @@ -49,13 +49,21 @@ public class ModelStoreImpl implements ModelStore { } @Override - public synchronized ModelImpl createModelForState(long state) { + public synchronized ModelImpl createModelForState(Version state) { var model = createModelWithoutInterpretations(state); - var interpretations = new HashMap>(stores.size()); + var interpretations = new LinkedHashMap>(stores.size()); + + int i=0; for (var entry : this.stores.entrySet()) { var symbol = entry.getKey(); - interpretations.put(symbol, VersionedInterpretation.of(model, symbol, entry.getValue(), state)); + interpretations.put(symbol, + VersionedInterpretation.of( + model, + symbol, + entry.getValue(), + ModelVersion.getInternalVersion(state,i++))); } + model.setInterpretations(interpretations); adaptModel(model); return model; @@ -69,16 +77,7 @@ public class ModelStoreImpl implements ModelStore { } @Override - public synchronized Set getStates() { - var iterator = stores.values().iterator(); - if (iterator.hasNext()) { - return Set.copyOf(iterator.next().getStates()); - } - return Set.of(0L); - } - - @Override - public synchronized ModelDiffCursor getDiffCursor(long from, long to) { + public synchronized ModelDiffCursor getDiffCursor(Version from, Version to) { var diffCursors = new HashMap>(); for (var entry : stores.entrySet()) { var representation = entry.getKey(); 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 new file mode 100644 index 00000000..cf3b7fc6 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/model/internal/ModelVersion.java @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.model.internal; + +import tools.refinery.store.map.Version; + +import java.util.Arrays; + +public record ModelVersion(Version[] mapVersions) implements Version{ + + public static Version getInternalVersion(Version modelVersion, int interpretationIndex) { + return ((ModelVersion)modelVersion).mapVersions()[interpretationIndex]; + } + + @Override + public int hashCode() { + return Arrays.hashCode(mapVersions); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ModelVersion that = (ModelVersion) o; + + return Arrays.equals(mapVersions, that.mapVersions); + } + + @Override + public String toString() { + return "ModelVersion{" + + "mapVersions=" + Arrays.toString(mapVersions) + + '}'; + } +} 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 404be65f..76e3baea 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 @@ -5,10 +5,7 @@ */ package tools.refinery.store.model.internal; -import tools.refinery.store.map.Cursor; -import tools.refinery.store.map.DiffCursor; -import tools.refinery.store.map.VersionedMap; -import tools.refinery.store.map.VersionedMapStore; +import tools.refinery.store.map.*; import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.InterpretationListener; import tools.refinery.store.model.Model; @@ -110,15 +107,14 @@ public class VersionedInterpretation implements Interpretation { } @Override - public DiffCursor getDiffCursor(long to) { + public DiffCursor getDiffCursor(Version to) { return map.getDiffCursor(to); } - public long commit() { + Version commit() { return map.commit(); } - - public void restore(long state) { + void restore(Version state) { if (!restoreListeners.isEmpty()) { var diffCursor = getDiffCursor(state); while (diffCursor.move()) { @@ -150,7 +146,7 @@ public class VersionedInterpretation implements Interpretation { } static VersionedInterpretation of(ModelImpl model, AnySymbol symbol, VersionedMapStore store, - long state) { + Version state) { @SuppressWarnings("unchecked") var typedSymbol = (Symbol) symbol; var map = store.createMap(state); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java index 5a4f8038..94259edc 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/DiffCursorFuzzTest.java @@ -10,13 +10,12 @@ import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import tools.refinery.store.map.DiffCursor; -import tools.refinery.store.map.VersionedMap; -import tools.refinery.store.map.VersionedMapStore; -import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; +import tools.refinery.store.map.*; import tools.refinery.store.map.tests.fuzz.utils.FuzzTestUtils; import tools.refinery.store.map.tests.utils.MapTestEnvironment; +import java.util.HashMap; +import java.util.Map; import java.util.Random; import java.util.stream.Stream; @@ -39,6 +38,7 @@ class DiffCursorFuzzTest { int commitFrequency, boolean commitBeforeDiffCursor) { int largestCommit = -1; + Map index2Version = new HashMap<>(); { // 1. build a map with versions @@ -55,8 +55,9 @@ class DiffCursorFuzzTest { fail(scenario + ":" + index + ": exception happened: " + exception); } if (index % commitFrequency == 0) { - long version = versioned.commit(); - largestCommit = (int) version; + Version version = versioned.commit(); + index2Version.put(index,version); + largestCommit = index; } if (index % 10000 == 0) System.out.println(scenario + ":" + index + "/" + steps + " building finished"); @@ -73,20 +74,20 @@ class DiffCursorFuzzTest { int index = i + 1; if (index % diffTravelFrequency == 0) { // diff-travel - long travelToVersion = r2.nextInt(largestCommit + 1); + int travelToVersion = r2.nextInt(largestCommit + 1); - VersionedMap oracle = store.createMap(travelToVersion); + VersionedMap oracle = store.createMap(index2Version.get(travelToVersion)); if(commitBeforeDiffCursor) { moving.commit(); } - DiffCursor diffCursor = moving.getDiffCursor(travelToVersion); + DiffCursor diffCursor = moving.getDiffCursor(index2Version.get(travelToVersion)); moving.putAll(diffCursor); moving.commit(); MapTestEnvironment.compareTwoMaps(scenario + ":c" + index, oracle, moving); - moving.restore(travelToVersion); + moving.restore(index2Version.get(travelToVersion)); } else { // random puts diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java index 9b2e591a..dfe46bae 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/MultiThreadTestRunnable.java @@ -13,6 +13,7 @@ import java.util.List; import java.util.Map; import java.util.Random; +import tools.refinery.store.map.Version; import tools.refinery.store.map.VersionedMap; import tools.refinery.store.map.VersionedMapStore; import tools.refinery.store.map.tests.utils.MapTestEnvironment; @@ -61,7 +62,7 @@ public class MultiThreadTestRunnable implements Runnable { // 1. build a map with versions Random r = new Random(seed); VersionedMap versioned = store.createMap(); - Map index2Version = new HashMap<>(); + Map index2Version = new HashMap<>(); for (int i = 0; i < steps; i++) { int index = i + 1; @@ -74,7 +75,7 @@ public class MultiThreadTestRunnable implements Runnable { logAndThrowError(scenario + ":" + index + ": exception happened: " + exception); } if (index % commitFrequency == 0) { - long version = versioned.commit(); + Version version = versioned.commit(); index2Version.put(i, version); } MapTestEnvironment.printStatus(scenario, index, steps, "building"); @@ -100,13 +101,12 @@ public class MultiThreadTestRunnable implements Runnable { MapTestEnvironment.compareTwoMaps(scenario + ":" + index, reference, versioned, null); // go back to a random state (probably created by another thread) - List states = new ArrayList<>(store.getStates()); - states.sort(Long::compare); + List states = new ArrayList<>(index2Version.values()); + //states.sort(Long::compare); Collections.shuffle(states, r2); - for (Long state : states.subList(0, Math.min(states.size(), 100))) { - long x = state; - versioned.restore(x); - var clean = store.createMap(x); + for (Version state : states.subList(0, Math.min(states.size(), 100))) { + versioned.restore(state); + var clean = store.createMap(state); MapTestEnvironment.compareTwoMaps(scenario + ":" + index, clean, versioned, null); } versioned.restore(index2Version.get(i)); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java index 0b399c3a..5c768788 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/RestoreFuzzTest.java @@ -10,6 +10,7 @@ import org.junit.jupiter.api.Timeout; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import tools.refinery.store.map.Version; import tools.refinery.store.map.VersionedMap; import tools.refinery.store.map.VersionedMapStore; import tools.refinery.store.map.VersionedMapStoreFactoryBuilder; @@ -40,7 +41,7 @@ class RestoreFuzzTest { // 1. build a map with versions Random r = new Random(seed); VersionedMap versioned = store.createMap(); - Map index2Version = new HashMap<>(); + Map index2Version = new HashMap<>(); for (int i = 0; i < steps; i++) { int index = i + 1; @@ -53,7 +54,7 @@ class RestoreFuzzTest { fail(scenario + ":" + index + ": exception happened: " + exception); } if (index % commitFrequency == 0) { - long version = versioned.commit(); + Version version = versioned.commit(); index2Version.put(i, version); } MapTestEnvironment.printStatus(scenario, index, steps, "building"); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java index b17766b7..299c94b1 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/SharedStoreFuzzTest.java @@ -19,6 +19,7 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import tools.refinery.store.map.ContinuousHashProvider; +import tools.refinery.store.map.Version; import tools.refinery.store.map.VersionedMapStore; import tools.refinery.store.map.internal.state.VersionedMapStoreStateImpl; import tools.refinery.store.map.internal.state.VersionedMapStateImpl; @@ -47,7 +48,7 @@ class SharedStoreFuzzTest { versioneds.add((VersionedMapStateImpl) store.createMap()); } - List> index2Version = new LinkedList<>(); + List> index2Version = new LinkedList<>(); for (int i = 0; i < stores.size(); i++) { index2Version.add(new HashMap<>()); } @@ -59,7 +60,7 @@ class SharedStoreFuzzTest { String nextValue = values[r.nextInt(values.length)]; versioneds.get(storeIndex).put(nextKey, nextValue); if (stepIndex % commitFrequency == 0) { - long version = versioneds.get(storeIndex).commit(); + Version version = versioneds.get(storeIndex).commit(); index2Version.get(storeIndex).put(i, version); } MapTestEnvironment.printStatus(scenario, stepIndex, steps, "building"); diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java index 4c3ecb09..ec04904e 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/fuzz/utils/FuzzTestCollections.java @@ -18,26 +18,35 @@ public final class FuzzTestCollections { public static final Object[] randomSeedOptions = {1}; public static final Object[] storeConfigs = { // State based + // Default VersionedMapStore.builder() - .stateBasedImmutableWhenCommitting(true) .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + // Evil hash code test VersionedMapStore.builder() - .stateBasedImmutableWhenCommitting(true) .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(true)) .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + // No weak hashmap test + VersionedMapStore.builder() + .versionFreeing(false) + .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) + .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + // Copy when committing, do not hurt the work copy, share between saves. VersionedMapStore.builder() .stateBasedImmutableWhenCommitting(false) .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.SHARED_NODE_CACHE), + // Copy when committing, do not hurt the work copy, do not share between states. VersionedMapStore.builder() .stateBasedImmutableWhenCommitting(false) .stateBasedHashProvider(MapTestEnvironment.prepareHashProvider(false)) .stateBasedSharingStrategy(VersionedMapStoreFactoryBuilder.SharingStrategy.NO_NODE_CACHE), // Delta based + // Set based transactions VersionedMapStore.builder() .deltaTransactionStrategy(VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy.SET), + // List based transactions VersionedMapStore.builder() .deltaTransactionStrategy(VersionedMapStoreFactoryBuilder.DeltaTransactionStrategy.LIST) }; diff --git a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java index 401f2866..b84df280 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java +++ b/subprojects/store/src/test/java/tools/refinery/store/map/tests/utils/MapTestEnvironment.java @@ -125,7 +125,7 @@ public class MapTestEnvironment { } } - public long commit(){ + public Version commit(){ return sut.commit(); } diff --git a/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java b/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java index 56b75804..dc7b776e 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/model/tests/ModelTest.java @@ -6,6 +6,7 @@ package tools.refinery.store.model.tests; import org.junit.jupiter.api.Test; +import tools.refinery.store.map.Version; import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.Symbol; @@ -120,7 +121,7 @@ class ModelTest { assertTrue(model.hasUncommittedChanges()); assertEquals(Model.NO_STATE_ID, model.getState()); - long state1 = model.commit(); + Version state1 = model.commit(); assertFalse(model.hasUncommittedChanges()); assertEquals(state1, model.getState()); @@ -134,7 +135,7 @@ class ModelTest { assertTrue(model.hasUncommittedChanges()); assertEquals(state1, model.getState()); - long state2 = model.commit(); + Version state2 = model.commit(); assertFalse(model.hasUncommittedChanges()); assertEquals(state2, model.getState()); -- cgit v1.2.3-54-g00ecf From 64f8706af45647b3015abc1bb1378c6be22a515c Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sat, 29 Jul 2023 16:16:02 +0200 Subject: Initial prototype of the StateCoderAdapter based on NeighbourhoodCalculator. No tests yet. --- subprojects/store/build.gradle.kts | 5 + .../store/statecoding/StateCodeCalculator.java | 10 ++ .../store/statecoding/StateCoderAdapter.java | 12 ++ .../store/statecoding/StateCoderBuilder.java | 29 +++++ .../store/statecoding/StateCoderStoreAdapter.java | 14 +++ .../internal/StateCoderAdapterImpl.java | 47 ++++++++ .../internal/StateCoderBuilderImpl.java | 48 ++++++++ .../internal/StateCoderStoreAdapterImpl.java | 34 ++++++ .../neighbourhood/NeighbourhoodCalculator.java | 129 +++++++++++++++++++++ .../statecoding/neighbourhood/ObjectCode.java | 55 +++++++++ 10 files changed, 383 insertions(+) create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculator.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderBuilder.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderStoreAdapter.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCode.java (limited to 'subprojects') diff --git a/subprojects/store/build.gradle.kts b/subprojects/store/build.gradle.kts index 2c485020..d653f01d 100644 --- a/subprojects/store/build.gradle.kts +++ b/subprojects/store/build.gradle.kts @@ -8,3 +8,8 @@ plugins { id("tools.refinery.gradle.java-library") id("tools.refinery.gradle.jmh") } + +dependencies { + implementation(libs.eclipseCollections) + implementation(libs.eclipseCollections.api) +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculator.java new file mode 100644 index 00000000..479b61ed --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculator.java @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +public interface StateCodeCalculator { + +} 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 new file mode 100644 index 00000000..8795fb68 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderAdapter.java @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +import tools.refinery.store.adapter.ModelAdapter; + +public interface StateCoderAdapter extends ModelAdapter { + int calculateHashCode(); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderBuilder.java new file mode 100644 index 00000000..2f37584f --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderBuilder.java @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +import tools.refinery.store.adapter.ModelAdapterBuilder; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.representation.AnySymbol; + +import java.util.Collection; +import java.util.List; + +public interface StateCoderBuilder extends ModelAdapterBuilder { + StateCoderBuilder exclude(AnySymbol symbol); + default StateCoderBuilder excludeAll(Collection symbols) { + for(var symbol : symbols) { + exclude(symbol); + } + return this; + } + default StateCoderBuilder excludeAll(AnySymbol... symbols) { + return excludeAll(List.of(symbols)); + } + + @Override + StateCoderStoreAdapter build(ModelStore store); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderStoreAdapter.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderStoreAdapter.java new file mode 100644 index 00000000..5946a162 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderStoreAdapter.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.model.Model; + +public interface StateCoderStoreAdapter extends ModelStoreAdapter { + @Override + StateCoderAdapter createModelAdapter(Model model); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java new file mode 100644 index 00000000..689db2e3 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.internal; + +import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.StateCoderAdapter; +import tools.refinery.store.statecoding.neighbourhood.NeighbourhoodCalculator; + +import java.util.Collection; +import java.util.List; + +public class StateCoderAdapterImpl implements StateCoderAdapter { + final ModelStoreAdapter storeAdapter; + final Model model; + final NeighbourhoodCalculator calculator; + + StateCoderAdapterImpl(ModelStoreAdapter storeAdapter, Model model, Collection> symbols) { + this.storeAdapter = storeAdapter; + this.model = model; + + List> interpretations = symbols.stream().map(model::getInterpretation).toList(); + calculator = new NeighbourhoodCalculator(interpretations); + } + + @Override + public Model getModel() { + return model; + } + + @Override + public ModelStoreAdapter getStoreAdapter() { + return storeAdapter; + } + + @Override + public int calculateHashCode() { + return calculator.calculate(); + } + + +} 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 new file mode 100644 index 00000000..700723f4 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderBuilderImpl.java @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.internal; + +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.representation.AnySymbol; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.StateCoderBuilder; +import tools.refinery.store.statecoding.StateCoderStoreAdapter; + +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Set; + +public class StateCoderBuilderImpl implements StateCoderBuilder { + Set excluded = new HashSet<>(); + + @Override + public StateCoderBuilder exclude(AnySymbol symbol) { + excluded.add(symbol); + return this; + } + + @Override + public boolean isConfigured() { + return true; + } + + @Override + public void configure(ModelStoreBuilder storeBuilder) { + // It does not modify the build process + } + + @Override + public StateCoderStoreAdapter build(ModelStore store) { + Set> symbols = new LinkedHashSet<>(); + for (AnySymbol symbol : store.getSymbols()) { + if (!excluded.contains(symbol) && (symbol instanceof Symbol typed)) { + symbols.add(typed); + } + } + return new StateCoderStoreAdapterImpl(store, symbols); + } +} 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 new file mode 100644 index 00000000..77d36e96 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderStoreAdapterImpl.java @@ -0,0 +1,34 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.internal; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.StateCoderAdapter; +import tools.refinery.store.statecoding.StateCoderStoreAdapter; + +import java.util.Collection; + +public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { + final ModelStore store; + final Collection> symbols; + + StateCoderStoreAdapterImpl(ModelStore store, Collection> symbols) { + this.store = store; + this.symbols = symbols; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public StateCoderAdapter createModelAdapter(Model model) { + return new StateCoderAdapterImpl(this,model,symbols); + } +} 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 new file mode 100644 index 00000000..24a7122e --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java @@ -0,0 +1,129 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.neighbourhood; + +import org.eclipse.collections.api.set.primitive.MutableLongSet; +import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple0; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Random; + +public class NeighbourhoodCalculator { + final List> nullImpactValues; + final LinkedHashMap, long[]> impactValues; + + public NeighbourhoodCalculator(List> interpretations) { + this.nullImpactValues = new ArrayList<>(); + this.impactValues = new LinkedHashMap<>(); + Random random = new Random(1); + + for (Interpretation interpretation : interpretations) { + int arity = interpretation.getSymbol().arity(); + if (arity == 0) { + nullImpactValues.add(interpretation); + } else { + long[] impact = new long[arity]; + for (int i = 0; i < arity; i++) { + impact[i] = random.nextLong(); + } + impactValues.put(interpretation, impact); + } + } + } + + public int calculate() { + ObjectCode previous = new ObjectCode(); + ObjectCode next = new ObjectCode(); + + int previousSize = 1; + long lastSum; + boolean grows; + + do{ + for (var impactValueEntry : this.impactValues.entrySet()) { + Interpretation interpretation = impactValueEntry.getKey(); + long[] impact = impactValueEntry.getValue(); + var cursor = interpretation.getAll(); + while (cursor.move()) { + Tuple tuple = cursor.getKey(); + Object value = cursor.getValue(); + long tupleHash = getTupleHash(tuple, value, previous); + addHash(next, tuple, impact, tupleHash); + } + } + + previous = next; + next = null; + lastSum = 0; + MutableLongSet codes = new LongHashSet(); + for (int i = 0; i < previous.getSize(); i++) { + long objectHash = previous.get(i); + codes.add(objectHash); + + final long shifted1 = objectHash>>> 32; + final long shifted2 = objectHash << 32; + lastSum += shifted1 + shifted2; + } + int nextSize = codes.size(); + grows = previousSize < nextSize; + previousSize = nextSize; + + if(grows) { + next = new ObjectCode(previous); + } + } while (grows); + + long result = 1; + for (var nullImpactValue : nullImpactValues) { + result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); + } + result += lastSum; + + return (int) result; + } + + protected long getTupleHash(Tuple tuple, Object value, ObjectCode objectCode) { + long result = (long) value; + int arity = tuple.getSize(); + if (arity == 1) { + result = result * 31 + objectCode.get(tuple.get(0)); + } else if (arity == 2) { + result = result * 31 + objectCode.get(tuple.get(0)); + result = result * 31 + objectCode.get(tuple.get(1)); + if (tuple.get(0) == tuple.get(1)) { + result++; + } + } else if (arity > 2) { + for (int i = 0; i < arity; i++) { + result = result * 31 + objectCode.get(tuple.get(i)); + } + } + return result; + } + + protected void addHash(ObjectCode objectCode, Tuple tuple, long[] impact, long tupleHashCode) { + if (tuple.getSize() == 1) { + addHash(objectCode, tuple.get(0), impact[0], tupleHashCode); + } else if (tuple.getSize() == 2) { + addHash(objectCode, tuple.get(0), impact[0], tupleHashCode); + addHash(objectCode, tuple.get(1), impact[1], tupleHashCode); + } else if (tuple.getSize() > 2) { + for (int i = 0; i < tuple.getSize(); i++) { + addHash(objectCode, tuple.get(i), impact[i], tupleHashCode); + } + } + } + + protected void addHash(ObjectCode objectCode, int o, long impact, long tupleHash) { + objectCode.set(o, objectCode.get(o) + tupleHash * impact); + } + +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCode.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCode.java new file mode 100644 index 00000000..594d2b3a --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCode.java @@ -0,0 +1,55 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.neighbourhood; + +public class ObjectCode { + private long[] vector; + private int size; + + public ObjectCode() { + vector = new long[10]; + size = 0; + } + + public ObjectCode(ObjectCode sameSize) { + this.vector = new long[sameSize.size]; + this.size = sameSize.size; + } + + private void ensureSize(int object) { + if(object >= size) { + size = object+1; + } + + if(object >= vector.length) { + int newLength = vector.length*2; + while(object >= newLength) { + newLength*=2; + } + + long[] newVector = new long[newLength]; + System.arraycopy(vector, 0, newVector, 0, vector.length); + this.vector = newVector; + } + } + + public long get(int object) { + if(object < vector.length) { + return vector[object]; + } else { + return 0; + } + } + + public void set(int object, long value) { + ensureSize(object); + vector[object]=value; + } + + public int getSize() { + return this.size; + } +} -- cgit v1.2.3-54-g00ecf From 4a528d6d5b5502c81a1cd094fcf9e706062b982c Mon Sep 17 00:00:00 2001 From: nagilooh Date: Tue, 25 Jul 2023 21:55:57 +0200 Subject: Add new test files --- subprojects/store-query-viatra/build.gradle.kts | 2 + .../refinery/store/query/dse/CRAExamplesTest.java | 135 +++++++++++++++++++++ .../tools/refinery/store/query/dse/DebugTest.java | 114 +++++++++++++++++ 3 files changed, 251 insertions(+) create mode 100644 subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java create mode 100644 subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/build.gradle.kts b/subprojects/store-query-viatra/build.gradle.kts index 85ea7e2e..1f804247 100644 --- a/subprojects/store-query-viatra/build.gradle.kts +++ b/subprojects/store-query-viatra/build.gradle.kts @@ -10,7 +10,9 @@ plugins { dependencies { implementation(libs.ecore) + implementation ("guru.nidi:graphviz-java:0.18.1") api(libs.viatra) api(project(":refinery-store-query")) api(project(":refinery-store-reasoning")) + api(project(":refinery-visualization")) } diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java new file mode 100644 index 00000000..8fe50a42 --- /dev/null +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java @@ -0,0 +1,135 @@ +package tools.refinery.store.query.dse; + +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.dse.internal.TransformationRule; +import tools.refinery.store.query.view.AnySymbolView; +import tools.refinery.store.query.view.KeyOnlyView; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; + +import static tools.refinery.store.query.literal.Literals.not; + +public class CRAExamplesTest { + private static final Symbol classModel = Symbol.of("ClassModel", 1); + private static final Symbol classElement = Symbol.of("ClassElement", 1); + private static final Symbol feature = Symbol.of("Feature", 1); + + private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); + private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); + + private static final Symbol features = Symbol.of("Features", 2); + private static final Symbol classes = Symbol.of("Classes", 2); + + private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); + private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); + private static final AnySymbolView featureView = new KeyOnlyView<>(feature); + private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); + private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); + private static final AnySymbolView featuresView = new KeyOnlyView<>(features); + private static final AnySymbolView classesView = new KeyOnlyView<>(classes); + + /*Example Transformation rules*/ + private static final RelationalQuery assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", + (builder, model, c, f) -> builder.clause( + classElementView.call(c), + classesView.call(model, c), + encapsulatesView.call(c, f) + )); + + private static final RelationalQuery assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f) + -> builder.clause((model, c1) -> List.of( + classModelView.call(model), + featureView.call(f), + classElementView.call(c2), + featuresView.call(model, f), + classesView.call(model, c1), + not(assignFeaturePreconditionHelper.call(model, c2, f)), + not(encapsulatesView.call(c2, f)) + ))); + + private static final RelationalQuery deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", + (builder, model, c) -> builder.clause((f) -> List.of( + classModelView.call(model), + classElementView.call(c), + featuresView.call(model, f), + not(encapsulatesView.call(c, f)) + ))); + + private static final RelationalQuery createClassPreconditionHelper = Query.of("CreateClassPreconditionHelper", + (builder, model, f, c) -> builder.clause( + classElementView.call(c), + classesView.call(model, c), + encapsulatesView.call(c, f) + )); + + private static final RelationalQuery createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model, f) -> builder.clause((c) -> List.of( + classModelView.call(model), + featureView.call(f), + not(createClassPreconditionHelper.call(model, f, c)) + ))); + + private static final RelationalQuery moveFeature = Query.of("MoveFeature", + (builder, c1, c2, f) -> builder.clause((model) -> List.of( + classModelView.call(model), + classElementView.call(c1), + classElementView.call(c2), + featureView.call(f), + classesView.call(model, c1), + classesView.call(model, c2), + featuresView.call(model, f), + encapsulatesView.call(c1, f) + ))); + + private static final TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", + assignFeaturePrecondition, + (model) -> { + var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); + return ((Tuple activation) -> { + var feature = activation.get(0); + var classElement = activation.get(1); + + isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); + }); + }); + + private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + dseAdapter.deleteObject(Tuple.of(classElement)); + }); + }); + + private static final TransformationRule createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var adapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var classElementInterpretation = model.getInterpretation(classElement); + var classesInterpretation = model.getInterpretation(classes); + var encapsulatesInterpretation = model.getInterpretation(encapsulates); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var feature = activation.get(1); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + classElementInterpretation.put(newClassElement, true); + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true); + }); + }); + +} diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java new file mode 100644 index 00000000..969afbcb --- /dev/null +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java @@ -0,0 +1,114 @@ +package tools.refinery.store.query.dse; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.dse.internal.TransformationRule; +import tools.refinery.store.query.dse.strategy.DepthFirstStrategy; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.query.view.AnySymbolView; +import tools.refinery.store.query.view.KeyOnlyView; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.ModelVisualizerAdapter; + +public class DebugTest { + private static final Symbol classModel = Symbol.of("ClassModel", 1); + private static final Symbol classElement = Symbol.of("ClassElement", 1); + private static final Symbol feature = Symbol.of("Feature", 1); + + private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); + private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); + + private static final Symbol features = Symbol.of("Features", 2); + private static final Symbol classes = Symbol.of("Classes", 2); + + private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); + private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); + private static final AnySymbolView featureView = new KeyOnlyView<>(feature); + private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); + private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); + private static final AnySymbolView featuresView = new KeyOnlyView<>(features); + private static final AnySymbolView classesView = new KeyOnlyView<>(classes); + + + @Test + void BFSTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createFeatureRule = new TransformationRule("CreateFeature", + createFeaturePrecondition, + (model) -> { + var featuresInterpretation = model.getInterpretation(features); + var featureInterpretation = model.getInterpretation(feature); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + featureInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition, createFeaturePrecondition)) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule, createFeatureRule) + .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() +// .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() +// .goOnOnlyIfFitnessIsBetter() + )) + .with(ModelVisualizerAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + + var states = dseAdapter.explore(); + var visualizer = model.getAdapter(ModelVisualizerAdapter.class); + for (var state : states) { + var visualization = visualizer.createVisualizationForModelState(state); + visualizer.saveVisualization(visualization, "test_output" + state.hashCode() + ".png"); + } + System.out.println("states size: " + states.size()); + System.out.println("states: " + states); + + } +} -- cgit v1.2.3-54-g00ecf From 68aa56964c736c2fc4fa4dcdae1af4d4db94bf7d Mon Sep 17 00:00:00 2001 From: nagilooh Date: Thu, 27 Jul 2023 12:12:26 +0200 Subject: Add visualization using DOT language --- .../visualization/ModelVisualizerAdapter.java | 11 ++ .../visualization/internal/FileFormat.java | 21 ++++ .../internal/ModelVisualizerAdapterImpl.java | 113 ++++++++++++++++++--- 3 files changed, 132 insertions(+), 13 deletions(-) create mode 100644 subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java (limited to 'subprojects') diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java index ae23e3bc..42f01242 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java @@ -3,6 +3,7 @@ package tools.refinery.visualization; import guru.nidi.graphviz.engine.Format; import guru.nidi.graphviz.model.MutableGraph; import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.visualization.internal.FileFormat; import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl; public interface ModelVisualizerAdapter extends ModelAdapter { @@ -16,7 +17,17 @@ public interface ModelVisualizerAdapter extends ModelAdapter { public MutableGraph createVisualizationForModelState(Long version); + public String createDotForCurrentModelState(); + + public String createDotForModelState(Long version); + public boolean saveVisualization(MutableGraph graph, String path); public boolean saveVisualization(MutableGraph graph, Format format, String path); + + public boolean saveDot(String dot, String filePath); + + public boolean renderDot(String dot, String filePath); + + public boolean renderDot(String dot, FileFormat format, String filePath); } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java new file mode 100644 index 00000000..43d6eb3f --- /dev/null +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java @@ -0,0 +1,21 @@ +package tools.refinery.visualization.internal; + +public enum FileFormat { + BMP("bmp"), + DOT("dot"), + JPEG("jpg"), + PDF("pdf"), + PLAIN("plain"), + PNG("png"), + SVG("svg"); + + private final String format; + + FileFormat(String format) { + this.format = format; + } + + public String getFormat() { + return format; + } +} diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java index 475ae416..7456d913 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -1,24 +1,51 @@ package tools.refinery.visualization.internal; +import guru.nidi.graphviz.attribute.GraphAttr; import guru.nidi.graphviz.attribute.Label; import guru.nidi.graphviz.engine.Format; import guru.nidi.graphviz.engine.Graphviz; import guru.nidi.graphviz.model.MutableGraph; +import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.Model; +import tools.refinery.store.representation.AnySymbol; +import tools.refinery.store.representation.TruthValue; import tools.refinery.visualization.ModelVisualizerAdapter; import tools.refinery.visualization.ModelVisualizerStoreAdapter; -import java.io.File; -import java.io.IOException; +import java.io.*; +import java.util.HashMap; +import java.util.Map; import static guru.nidi.graphviz.model.Factory.*; public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { private final Model model; private final ModelVisualizerStoreAdapter storeAdapter; + private final Map> interpretations; public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { this.model = model; this.storeAdapter = storeAdapter; + this.interpretations = new HashMap<>(); + for (var symbol : storeAdapter.getStore().getSymbols()) { + var arity = symbol.arity(); + if (arity < 1 || arity > 2) { + continue; + } + var interpretation = model.getInterpretation(symbol); + var valueType = symbol.valueType(); + Interpretation castInterpretation; + if (valueType == Boolean.class) { + castInterpretation = (Interpretation) interpretation; + } + // TODO: support TruthValue +// else if (valueType == TruthValue.class) { +// castInterpretation = (Interpretation) interpretation; +// } + else { + continue; + } + interpretations.put(symbol, castInterpretation); + } } @Override @@ -33,19 +60,10 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { @Override public MutableGraph createVisualizationForCurrentModelState() { - var interpretations = model.getInterpretations(); - MutableGraph graph = mutGraph("model").setDirected(true); + var graph = mutGraph("model").setDirected(true).graphAttrs().add(GraphAttr.dpi(100)); for (var entry : interpretations.entrySet()) { var key = entry.getKey(); var arity = key.arity(); - if (arity < 1 || arity > 2) { - continue; - } - var valueType = key.valueType(); - // TODO: support TruthValue - if (valueType != Boolean.class) { - continue; - } var cursor = entry.getValue().getAll(); while (cursor.move()) { if (arity == 1) { @@ -65,7 +83,41 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { public MutableGraph createVisualizationForModelState(Long version) { var currentVersion = model.getState(); model.restore(version); - MutableGraph graph = createVisualizationForCurrentModelState(); + var graph = createVisualizationForCurrentModelState(); + model.restore(currentVersion); + return graph; + } + + @Override + public String createDotForCurrentModelState() { + var sb = new StringBuilder(); + sb.append("digraph model {\n"); + for (var entry : interpretations.entrySet()) { + var key = entry.getKey(); + var arity = key.arity(); + var cursor = entry.getValue().getAll(); + while (cursor.move()) { + if (arity == 1) { + var id = cursor.getKey().get(0); + sb.append("\t").append(id).append(" [label=\"").append(key.name()).append(": ").append(id) + .append("\"]\n"); + } else { + var from = cursor.getKey().get(0); + var to = cursor.getKey().get(1); + sb.append("\t").append(from).append(" -> ").append(to).append(" [label=\"").append(key.name()) + .append("\"]\n"); + } + } + } + sb.append("}"); + return sb.toString(); + } + + @Override + public String createDotForModelState(Long version) { + var currentVersion = model.getState(); + model.restore(version); + var graph = createDotForCurrentModelState(); model.restore(currentVersion); return graph; } @@ -85,4 +137,39 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return false; } } + + @Override + public boolean saveDot(String dot, String filePath) { + File file = new File(filePath); + file.getParentFile().mkdirs(); + + try (FileWriter writer = new FileWriter(file)) { + writer.write(dot); + } catch (Exception e) { + e.printStackTrace(); + return false; + } + return true; + } + + @Override + public boolean renderDot(String dot, String filePath) { + return renderDot(dot, FileFormat.SVG, filePath); + } + + @Override + public boolean renderDot(String dot, FileFormat format, String filePath) { + try { + Process process = new ProcessBuilder("dot", "-T" + format.getFormat(), "-o", filePath).start(); + + OutputStream osToProcess = process.getOutputStream(); + PrintWriter pwToProcess = new PrintWriter(osToProcess); + pwToProcess.write(dot); + pwToProcess.close(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + return true; + } } -- cgit v1.2.3-54-g00ecf From 261d7d1a5a60dc46dc243118e55adf6c4bb8c9f8 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Thu, 27 Jul 2023 12:15:58 +0200 Subject: Add seed option for random --- .../store/query/dse/DesignSpaceExplorationAdapter.java | 5 +++++ .../query/dse/internal/DesignSpaceExplorationAdapterImpl.java | 10 ++++++++++ 2 files changed, 15 insertions(+) (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java index e7ce7b2c..4cfca210 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java @@ -10,6 +10,7 @@ import tools.refinery.store.tuple.Tuple1; import java.util.Collection; import java.util.List; +import java.util.Random; public interface DesignSpaceExplorationAdapter extends ModelAdapter { @Override @@ -52,4 +53,8 @@ public interface DesignSpaceExplorationAdapter extends ModelAdapter { public ObjectiveComparatorHelper getObjectiveComparatorHelper(); public void restoreTrajectory(List trajectory); + + public void setRandom(Random random); + + public void setRandom(long seed); } diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java index eb2fc063..c4c51e79 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -141,6 +141,16 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } + @Override + public void setRandom(Random random) { + this.random = random; + } + + @Override + public void setRandom(long seed) { + this.random = new Random(seed); + } + @Override public Fitness calculateFitness() { Fitness result = new Fitness(); -- cgit v1.2.3-54-g00ecf From f5604eebaf63e4227f57cdc27644ac04db26caf4 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Thu, 27 Jul 2023 12:17:23 +0200 Subject: Add trivial test cases for DSE Strategies --- .../query/dse/DesignSpaceExplorationTest.java | 382 ++++++++++++++++++++- 1 file changed, 376 insertions(+), 6 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java index 06e2e076..59775b43 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java @@ -1,6 +1,5 @@ package tools.refinery.store.query.dse; -import guru.nidi.graphviz.engine.Format; import org.junit.jupiter.api.Test; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; @@ -15,12 +14,7 @@ import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; import tools.refinery.visualization.ModelVisualizerAdapter; -import java.util.List; -import java.util.Map; - import static org.junit.jupiter.api.Assertions.assertEquals; -import static tools.refinery.store.query.literal.Literals.not; -import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults; public class DesignSpaceExplorationTest { // private static final Symbol namedElement = Symbol.of("NamedElement", 1); @@ -114,4 +108,380 @@ public class DesignSpaceExplorationTest { assertEquals(5, dseAdapter.getModelSize()); } + @Test + void DFSTrivialTest() { + var store = ModelStore.builder() + .symbols(classModel) + .with(ViatraModelQueryAdapter.builder()) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .strategy(new DepthFirstStrategy(0))) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + + var states = dseAdapter.explore(); + assertEquals(1, states.size()); + assertEquals(0, states.iterator().next()); + } + + @Test + void DFSOneRuleTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule) + .strategy(new DepthFirstStrategy(4) + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + assertEquals(1, states.size()); + assertEquals(0, states.iterator().next()); + } + + @Test + void DFSContinueTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule) + .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + var iterator = states.iterator(); + assertEquals(5, states.size()); + assertEquals(0, iterator.next()); + assertEquals(1, iterator.next()); + assertEquals(2, iterator.next()); + assertEquals(3, iterator.next()); + assertEquals(4, iterator.next()); + } + + @Test + void DFSCompletenessTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createFeatureRule = new TransformationRule("CreateFeature", + createFeaturePrecondition, + (model) -> { + var featuresInterpretation = model.getInterpretation(features); + var featureInterpretation = model.getInterpretation(feature); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + featureInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition, createFeaturePrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule, createFeatureRule) + .strategy(new DepthFirstStrategy(10).continueIfHardObjectivesFulfilled() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + assertEquals(2047, states.size()); + } + + @Test + void BFSTrivialTest() { + var store = ModelStore.builder() + .symbols(classModel) + .with(ViatraModelQueryAdapter.builder()) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .strategy(new BestFirstStrategy(0))) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + + var states = dseAdapter.explore(); + assertEquals(1, states.size()); + assertEquals(0, states.iterator().next()); + } + + @Test + void BFSOneRuleTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule) + .strategy(new BestFirstStrategy(4) + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + assertEquals(1, states.size()); + assertEquals(0, states.iterator().next()); + } + + @Test + void BFSContinueTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule) + .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + var iterator = states.iterator(); + assertEquals(5, states.size()); + assertEquals(0, iterator.next()); + assertEquals(1, iterator.next()); + assertEquals(2, iterator.next()); + assertEquals(3, iterator.next()); + assertEquals(4, iterator.next()); + } + + @Test + void BFSCompletenessTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createFeatureRule = new TransformationRule("CreateFeature", + createFeaturePrecondition, + (model) -> { + var featuresInterpretation = model.getInterpretation(features); + var featureInterpretation = model.getInterpretation(feature); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + featureInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition, createFeaturePrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule, createFeatureRule) + .strategy(new BestFirstStrategy(10).continueIfHardObjectivesFulfilled() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + assertEquals(2047, states.size()); + } + } -- cgit v1.2.3-54-g00ecf From 00c8fcf947d3f5c375d49a907bcefa5d8e1dd8ea Mon Sep 17 00:00:00 2001 From: nagilooh Date: Tue, 1 Aug 2023 12:41:45 +0200 Subject: Update visualization - Replace guru.nidi:graphviz-java with DOT based solution - Draws the design space as well as the model states - Design space nodes link to visualization of the sate --- subprojects/store-query-viatra/build.gradle.kts | 1 - .../DesignSpaceExplorationAdapterImpl.java | 21 ++++ .../tools/refinery/store/query/dse/DebugTest.java | 15 +-- subprojects/visualization/build.gradle.kts | 1 - .../visualization/ModelVisualizerAdapter.java | 23 ++-- .../internal/ModelVisualizerAdapterImpl.java | 120 +++++++++++---------- 6 files changed, 109 insertions(+), 72 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/build.gradle.kts b/subprojects/store-query-viatra/build.gradle.kts index 1f804247..ef73e10a 100644 --- a/subprojects/store-query-viatra/build.gradle.kts +++ b/subprojects/store-query-viatra/build.gradle.kts @@ -10,7 +10,6 @@ plugins { dependencies { implementation(libs.ecore) - implementation ("guru.nidi:graphviz-java:0.18.1") api(libs.viatra) api(project(":refinery-store-query")) api(project(":refinery-store-reasoning")) diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java index c4c51e79..0fcc719d 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -15,6 +15,7 @@ import tools.refinery.store.query.resultset.ResultSet; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple1; +import tools.refinery.visualization.ModelVisualizerAdapter; import java.util.*; @@ -40,6 +41,8 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration private Random random = new Random(); private Objective[][] leveledObjectives; private boolean isNewState = false; + private final boolean isVisualizationEnabled; + private final ModelVisualizerAdapter modelVisualizerAdapter; public List getTrajectory() { return new LinkedList<>(trajectory); @@ -66,6 +69,8 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration statesAndUntraversedActivations = new HashMap<>(); statesAndTraversedActivations = new HashMap<>(); strategy = storeAdapter.getStrategy(); + modelVisualizerAdapter = model.tryGetAdapter(ModelVisualizerAdapter.class).orElse(null); + isVisualizationEnabled = modelVisualizerAdapter != null; } @@ -129,6 +134,10 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration if (trajectory.size() < 2) { return false; } + if (isVisualizationEnabled) { + modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 1), + trajectory.get(trajectory.size() - 2), "backtrack"); + } model.restore(trajectory.get(trajectory.size() - 2)); trajectory.remove(trajectory.size() - 1); return true; @@ -137,6 +146,10 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration @Override public void restoreTrajectory(List trajectory) { model.restore(trajectory.get(trajectory.size() - 1)); +// if (isVisualizationEnabled) { +// modelVisualizerAdapter.addTransition(this.trajectory.get(trajectory.size() - 1), +// trajectory.get(trajectory.size() - 1), "restore"); +// } this.trajectory = trajectory; } @@ -173,6 +186,9 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration public void newSolution() { var state = model.getState(); solutions.add(state); + if (isVisualizationEnabled) { + modelVisualizerAdapter.addSolution(state); + } } @Override @@ -212,6 +228,11 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration isNewState = !statesAndUntraversedActivations.containsKey(newState); statesAndUntraversedActivations.put(newState, getAllActivations()); statesAndTraversedActivations.put(newState, new HashSet<>()); + if (isVisualizationEnabled) { + modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 2), + trajectory.get(trajectory.size() - 1), activation.transformationRule().getName(), + activation.activation()); + } return true; } diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java index 969afbcb..821be7e6 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java @@ -5,6 +5,7 @@ import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dse.internal.TransformationRule; +import tools.refinery.store.query.dse.strategy.BestFirstStrategy; import tools.refinery.store.query.dse.strategy.DepthFirstStrategy; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; @@ -12,6 +13,7 @@ import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.internal.FileFormat; public class DebugTest { private static final Symbol classModel = Symbol.of("ClassModel", 1); @@ -83,30 +85,31 @@ public class DebugTest { .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) .with(ViatraModelQueryAdapter.builder() .queries(createClassPrecondition, createFeaturePrecondition)) + .with(ModelVisualizerAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule, createFeatureRule) .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() // .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() // .goOnOnlyIfFitnessIsBetter() )) - .with(ModelVisualizerAdapter.builder()) .build(); var model = store.createEmptyModel(); var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// dseAdapter.setRandom(1); var queryEngine = model.getAdapter(ModelQueryAdapter.class); var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); + var classElementInterpretation = model.getInterpretation(classElement); + var modelElement = dseAdapter.createObject(); + modelElementInterpretation.put(modelElement, true); + classElementInterpretation.put(modelElement, true); queryEngine.flushChanges(); var states = dseAdapter.explore(); var visualizer = model.getAdapter(ModelVisualizerAdapter.class); - for (var state : states) { - var visualization = visualizer.createVisualizationForModelState(state); - visualizer.saveVisualization(visualization, "test_output" + state.hashCode() + ".png"); - } + visualizer.renderDesignSpace("test_output", FileFormat.SVG); System.out.println("states size: " + states.size()); System.out.println("states: " + states); diff --git a/subprojects/visualization/build.gradle.kts b/subprojects/visualization/build.gradle.kts index 97b1688a..abad0491 100644 --- a/subprojects/visualization/build.gradle.kts +++ b/subprojects/visualization/build.gradle.kts @@ -9,6 +9,5 @@ plugins { } dependencies { - implementation ("guru.nidi:graphviz-java:0.18.1") api(project(":refinery-store-query")) } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java index 42f01242..2f71d4f3 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java @@ -1,8 +1,7 @@ package tools.refinery.visualization; -import guru.nidi.graphviz.engine.Format; -import guru.nidi.graphviz.model.MutableGraph; import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.tuple.Tuple; import tools.refinery.visualization.internal.FileFormat; import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl; @@ -13,21 +12,25 @@ public interface ModelVisualizerAdapter extends ModelAdapter { return new ModelVisualizerBuilderImpl(); } - public MutableGraph createVisualizationForCurrentModelState(); - - public MutableGraph createVisualizationForModelState(Long version); - public String createDotForCurrentModelState(); public String createDotForModelState(Long version); - public boolean saveVisualization(MutableGraph graph, String path); - - public boolean saveVisualization(MutableGraph graph, Format format, String path); - public boolean saveDot(String dot, String filePath); public boolean renderDot(String dot, String filePath); public boolean renderDot(String dot, FileFormat format, String filePath); + + public void addTransition(Long from, Long to, String action); + + + public void addTransition(Long from, Long to, String action, Tuple activation); + public void addSolution(Long state); + + public boolean saveDesignSpace(String path); + + public boolean renderDesignSpace(String path); + + public boolean renderDesignSpace(String path, FileFormat format); } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java index 7456d913..9a284e24 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -1,14 +1,9 @@ package tools.refinery.visualization.internal; -import guru.nidi.graphviz.attribute.GraphAttr; -import guru.nidi.graphviz.attribute.Label; -import guru.nidi.graphviz.engine.Format; -import guru.nidi.graphviz.engine.Graphviz; -import guru.nidi.graphviz.model.MutableGraph; import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.Model; import tools.refinery.store.representation.AnySymbol; -import tools.refinery.store.representation.TruthValue; +import tools.refinery.store.tuple.Tuple; import tools.refinery.visualization.ModelVisualizerAdapter; import tools.refinery.visualization.ModelVisualizerStoreAdapter; @@ -16,12 +11,13 @@ import java.io.*; import java.util.HashMap; import java.util.Map; -import static guru.nidi.graphviz.model.Factory.*; - public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { private final Model model; private final ModelVisualizerStoreAdapter storeAdapter; private final Map> interpretations; + private final StringBuilder designSpaceBuilder = new StringBuilder(); + private int transitionCounter = 0; + public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { this.model = model; this.storeAdapter = storeAdapter; @@ -46,6 +42,7 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { } interpretations.put(symbol, castInterpretation); } + designSpaceBuilder.append("digraph designSpace {\n"); } @Override @@ -58,36 +55,6 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return storeAdapter; } - @Override - public MutableGraph createVisualizationForCurrentModelState() { - var graph = mutGraph("model").setDirected(true).graphAttrs().add(GraphAttr.dpi(100)); - for (var entry : interpretations.entrySet()) { - var key = entry.getKey(); - var arity = key.arity(); - var cursor = entry.getValue().getAll(); - while (cursor.move()) { - if (arity == 1) { - var id = cursor.getKey().get(0); - graph.add(mutNode(String.valueOf(id)).add("label", key.name() + ": " + id)); - } else { - var from = cursor.getKey().get(0); - var to = cursor.getKey().get(1); - graph.add(mutNode(String.valueOf(from)).addLink(to(mutNode(String.valueOf(to))).with(Label.of(key.name())))); - } - } - } - return graph; - } - - @Override - public MutableGraph createVisualizationForModelState(Long version) { - var currentVersion = model.getState(); - model.restore(version); - var graph = createVisualizationForCurrentModelState(); - model.restore(currentVersion); - return graph; - } - @Override public String createDotForCurrentModelState() { var sb = new StringBuilder(); @@ -122,22 +89,6 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return graph; } - @Override - public boolean saveVisualization(MutableGraph graph, String path) { - return saveVisualization(graph, Format.PNG, path); - } - - @Override - public boolean saveVisualization(MutableGraph graph, Format format, String path) { - try { - Graphviz.fromGraph(graph).render(format).toFile(new File(path)); - return true; - } catch (IOException e) { - e.printStackTrace(); - return false; - } - } - @Override public boolean saveDot(String dot, String filePath) { File file = new File(filePath); @@ -172,4 +123,65 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { } return true; } + + @Override + public void addTransition(Long from, Long to, String action) { + designSpaceBuilder.append(from).append(" -> ").append(to).append(" [label=\"").append(transitionCounter++) + .append(": ").append(action).append("\"]\n"); + + } + + @Override + public void addTransition(Long from, Long to, String action, Tuple activation) { + designSpaceBuilder.append(from).append(" -> ").append(to).append(" [label=\"").append(transitionCounter++) + .append(": ").append(action).append(" / "); + + + for (int i = 0; i < activation.getSize(); i++) { + designSpaceBuilder.append(activation.get(i)); + if (i < activation.getSize() - 1) { + designSpaceBuilder.append(", "); + } + } + designSpaceBuilder.append("\"]\n"); + } + + @Override + public void addSolution(Long state) { + designSpaceBuilder.append(state).append(" [shape = doublecircle]\n"); + } + + private String buildDesignSpaceDot() { + for (var state : storeAdapter.getStore().getStates()) { + designSpaceBuilder.append(state).append(" [URL=\"./").append(state).append(".svg\"]\n"); + } + designSpaceBuilder.append("}"); + return designSpaceBuilder.toString(); + } + + @Override + public boolean saveDesignSpace(String path) { + saveDot(buildDesignSpaceDot(), path + "/designSpace.dot"); + for (var state : storeAdapter.getStore().getStates()) { + saveDot(createDotForModelState(state), path + "/" + state + ".dot"); + } + return true; + } + + @Override + public boolean renderDesignSpace(String path) { + return renderDesignSpace(path, FileFormat.SVG); + } + + @Override + public boolean renderDesignSpace(String path, FileFormat format) { + for (var state : storeAdapter.getStore().getStates()) { + var stateDot = createDotForModelState(state); + saveDot(stateDot, path + "/" + state + ".dot"); + renderDot(stateDot, path + "/" + state + "." + format.getFormat()); + } + var designSpaceDot = buildDesignSpaceDot(); + saveDot(designSpaceDot, path + "/designSpace.dot"); + return renderDot(designSpaceDot, format, path + "/designSpace." + format.getFormat()); + } } -- cgit v1.2.3-54-g00ecf From e21e289dd62d1499cb63811ba0a6da0c684b0d95 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Tue, 1 Aug 2023 13:00:21 +0200 Subject: Change Set to LinkedHashSet, remove unused methods --- .../query/dse/DesignSpaceExplorationBuilder.java | 11 -------- .../DesignSpaceExplorationAdapterImpl.java | 27 ++++++++++---------- .../DesignSpaceExplorationBuilderImpl.java | 23 +++-------------- .../DesignSpaceExplorationStoreAdapterImpl.java | 29 ++++++++-------------- .../query/dse/internal/TransformationRule.java | 7 +++--- 5 files changed, 31 insertions(+), 66 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java index 0ec6f599..58eac681 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java @@ -10,15 +10,6 @@ import java.util.Collection; import java.util.List; public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { - default DesignSpaceExplorationBuilder stopConditions(AnyQuery... stopConditions) { - return stopConditions(List.of(stopConditions)); - } - - default DesignSpaceExplorationBuilder stopConditions(Collection stopConditions) { - stopConditions.forEach(this::stopCondition); - return this; - } - default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRules) { return transformations(List.of(transformationRules)); } @@ -46,8 +37,6 @@ public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { return this; } - DesignSpaceExplorationBuilder stopCondition(AnyQuery stopCondition); - DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule); DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint); DesignSpaceExplorationBuilder objective(Objective objective); diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java index 0fcc719d..35f94dce 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -24,20 +24,19 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration private final Model model; private final ModelQueryAdapter queryEngine; private final DesignSpaceExplorationStoreAdapterImpl storeAdapter; - private final Set transformationRules; - private final Set globalConstraints; + private final LinkedHashSet transformationRules; + private final LinkedHashSet globalConstraints; private final List objectives; - private final Set> globalConstraintResultSets = new HashSet<>(); + private final LinkedHashSet> globalConstraintResultSets = new LinkedHashSet<>(); private final Interpretation sizeInterpretation; private final Strategy strategy; private ObjectiveComparatorHelper objectiveComparatorHelper; private List trajectory = new LinkedList<>(); private Fitness lastFitness; - private final Set solutions = new HashSet<>(); -// private Map> statesAndActivations; - private Map> statesAndUntraversedActivations; - private Map> statesAndTraversedActivations; + private final LinkedHashSet solutions = new LinkedHashSet<>(); + private Map> statesAndUntraversedActivations; + private Map> statesAndTraversedActivations; private Random random = new Random(); private Objective[][] leveledObjectives; private boolean isNewState = false; @@ -85,11 +84,11 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } @Override - public Collection explore() { + public LinkedHashSet explore() { var state = model.commit(); trajectory.add(state); statesAndUntraversedActivations.put(state, getAllActivations()); - statesAndTraversedActivations.put(state, new HashSet<>()); + statesAndTraversedActivations.put(state, new LinkedHashSet<>()); strategy.initStrategy(this); strategy.explore(); return solutions; @@ -196,9 +195,9 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration return trajectory.size() - 1; } - public Collection getUntraversedActivations() { + public LinkedHashSet getUntraversedActivations() { // return statesAndUntraversedActivations.get(model.getState()); - List untraversedActivations = new ArrayList<>(); + LinkedHashSet untraversedActivations = new LinkedHashSet<>(); for (Activation activation : getAllActivations()) { if (!statesAndTraversedActivations.get(model.getState()).contains(activation)) { untraversedActivations.add(activation); @@ -227,7 +226,7 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration trajectory.add(newState); isNewState = !statesAndUntraversedActivations.containsKey(newState); statesAndUntraversedActivations.put(newState, getAllActivations()); - statesAndTraversedActivations.put(newState, new HashSet<>()); + statesAndTraversedActivations.put(newState, new LinkedHashSet<>()); if (isVisualizationEnabled) { modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 2), trajectory.get(trajectory.size() - 1), activation.transformationRule().getName(), @@ -257,8 +256,8 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration return trajectory.contains(model.getState()); } - public Collection getAllActivations() { - Collection result = new HashSet<>(); + public LinkedHashSet getAllActivations() { + LinkedHashSet result = new LinkedHashSet<>(); for (var rule : transformationRules) { result.addAll(rule.getAllActivations()); } diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java index 79864de0..638ec641 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java @@ -3,7 +3,6 @@ package tools.refinery.store.query.dse.internal; import tools.refinery.store.adapter.AbstractModelAdapterBuilder; import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.dnf.AnyQuery; import tools.refinery.store.query.dnf.RelationalQuery; import tools.refinery.store.query.dse.DesignSpaceExplorationBuilder; import tools.refinery.store.query.dse.Strategy; @@ -12,29 +11,19 @@ import tools.refinery.store.query.dse.objectives.Objective; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; -import java.util.Set; public class DesignSpaceExplorationBuilderImpl extends AbstractModelAdapterBuilder implements DesignSpaceExplorationBuilder { - - private final Set stopConditionSpecifications = new LinkedHashSet<>(); - private final Set transformationSpecifications = new LinkedHashSet<>(); - private final Set globalConstraints = new LinkedHashSet<>(); + private final LinkedHashSet transformationSpecifications = new LinkedHashSet<>(); + private final LinkedHashSet globalConstraints = new LinkedHashSet<>(); private final List objectives = new LinkedList<>(); private Strategy strategy; @Override protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) { - return new DesignSpaceExplorationStoreAdapterImpl(store, stopConditionSpecifications, - transformationSpecifications, globalConstraints, objectives, strategy); - } - - @Override - public DesignSpaceExplorationBuilder stopCondition(AnyQuery stopCondition) { - checkNotConfigured(); - stopConditionSpecifications.add(stopCondition); - return this; + return new DesignSpaceExplorationStoreAdapterImpl(store, transformationSpecifications, globalConstraints, + objectives, strategy); } @Override @@ -65,10 +54,6 @@ public class DesignSpaceExplorationBuilderImpl return this; } - public Set getStopConditionSpecifications() { - return stopConditionSpecifications; - } - @Override protected void doConfigure(ModelStoreBuilder storeBuilder) { storeBuilder.symbols(DesignSpaceExplorationAdapterImpl.NODE_COUNT_SYMBOL); diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java index 9c2fa4d6..f58715b7 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java @@ -1,31 +1,28 @@ package tools.refinery.store.query.dse.internal; import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.query.dnf.AnyQuery; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.query.dse.DesignSpaceExplorationStoreAdapter; import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.query.dse.DesignSpaceExplorationStoreAdapter; import tools.refinery.store.query.dse.Strategy; import tools.refinery.store.query.dse.objectives.Objective; +import java.util.LinkedHashSet; import java.util.List; -import java.util.Set; public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { private final ModelStore store; - private final Set stopConditionSpecifications; - private final Set transformationSpecifications; - private final Set globalConstraints; + private final LinkedHashSet transformationSpecifications; + private final LinkedHashSet globalConstraints; private final List objectives; private final Strategy strategy; - public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, Set stopConditionSpecifications, - Set transformationSpecifications, - Set globalConstraints, List objectives, - Strategy strategy) { + public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, + LinkedHashSet transformationSpecifications, + LinkedHashSet globalConstraints, + List objectives, Strategy strategy) { this.store = store; - this.stopConditionSpecifications = stopConditionSpecifications; this.transformationSpecifications = transformationSpecifications; this.globalConstraints = globalConstraints; this.objectives = objectives; @@ -42,15 +39,11 @@ public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplor return new DesignSpaceExplorationAdapterImpl(model, this); } - public Set getStopConditionSpecifications() { - return stopConditionSpecifications; - } - - public Set getTransformationSpecifications() { + public LinkedHashSet getTransformationSpecifications() { return transformationSpecifications; } - public Set getGlobalConstraints() { + public LinkedHashSet getGlobalConstraints() { return globalConstraints; } diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java index c46ff712..950ca930 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java @@ -9,9 +9,8 @@ import tools.refinery.store.query.resultset.OrderedResultSet; import tools.refinery.store.query.resultset.ResultSet; import tools.refinery.store.tuple.Tuple; -import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.Random; -import java.util.Set; public class TransformationRule { @@ -66,8 +65,8 @@ public class TransformationRule { return activations; } - public Set getAllActivations() { - var result = new HashSet(); + public LinkedHashSet getAllActivations() { + var result = new LinkedHashSet(); var cursor = activations.getAll(); while (cursor.move()) { result.add(new Activation(this, cursor.getKey())); -- cgit v1.2.3-54-g00ecf From f123b148f01982ee3068f4be91c44241e0812830 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Wed, 2 Aug 2023 11:57:12 +0200 Subject: Remove objective levels --- .../DesignSpaceExplorationAdapterImpl.java | 9 +- .../store/query/dse/objectives/BaseObjective.java | 16 -- .../dse/objectives/LeveledObjectivesHelper.java | 114 --------- .../store/query/dse/objectives/Objective.java | 10 - .../dse/objectives/ObjectiveComparatorHelper.java | 276 +++++---------------- .../query/dse/objectives/TrajectoryFitness.java | 71 ------ 6 files changed, 59 insertions(+), 437 deletions(-) delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/LeveledObjectivesHelper.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/TrajectoryFitness.java (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java index 35f94dce..fbed0465 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -8,7 +8,6 @@ import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; import tools.refinery.store.query.dse.DesignSpaceExplorationStoreAdapter; import tools.refinery.store.query.dse.Strategy; import tools.refinery.store.query.dse.objectives.Fitness; -import tools.refinery.store.query.dse.objectives.LeveledObjectivesHelper; import tools.refinery.store.query.dse.objectives.Objective; import tools.refinery.store.query.dse.objectives.ObjectiveComparatorHelper; import tools.refinery.store.query.resultset.ResultSet; @@ -38,7 +37,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration private Map> statesAndUntraversedActivations; private Map> statesAndTraversedActivations; private Random random = new Random(); - private Objective[][] leveledObjectives; private boolean isNewState = false; private final boolean isVisualizationEnabled; private final ModelVisualizerAdapter modelVisualizerAdapter; @@ -64,7 +62,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } objectives = storeAdapter.getObjectives(); - leveledObjectives = new LeveledObjectivesHelper(objectives).initLeveledObjectives(); statesAndUntraversedActivations = new HashMap<>(); statesAndTraversedActivations = new HashMap<>(); strategy = storeAdapter.getStrategy(); @@ -275,12 +272,8 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration public ObjectiveComparatorHelper getObjectiveComparatorHelper() { if (objectiveComparatorHelper == null) { - objectiveComparatorHelper = new ObjectiveComparatorHelper(leveledObjectives); + objectiveComparatorHelper = new ObjectiveComparatorHelper(objectives); } return objectiveComparatorHelper; } - - public Objective[][] getLeveledObjectives() { - return leveledObjectives; - } } diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java index fd8529f7..db8d5d7e 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java @@ -24,7 +24,6 @@ public abstract class BaseObjective implements Objective { protected final String name; protected Comparator comparator = Comparators.HIGHER_IS_BETTER; - protected int level = 0; protected double fitnessConstraint; protected boolean isThereFitnessConstraint = false; @@ -50,21 +49,6 @@ public abstract class BaseObjective implements Objective { return comparator; } - @Override - public void setLevel(int level) { - this.level = level; - } - - @Override - public int getLevel() { - return level; - } - - public BaseObjective withLevel(int level) { - setLevel(level); - return this; - } - public BaseObjective withComparator(Comparator comparator) { setComparator(comparator); return this; diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/LeveledObjectivesHelper.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/LeveledObjectivesHelper.java deleted file mode 100644 index 6c4e7837..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/LeveledObjectivesHelper.java +++ /dev/null @@ -1,114 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Andras Szabolcs Nagy and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.query.dse.objectives; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Comparator; -import java.util.List; - -public class LeveledObjectivesHelper { - - private List objectives = new ArrayList(); - private Objective[][] leveledObjectives; - - public LeveledObjectivesHelper(List objectives) { - this.objectives = objectives; - } - - public Objective[][] initLeveledObjectives() { - if (objectives.isEmpty()) { - leveledObjectives = new Objective[0][0]; - return leveledObjectives; - } - - int level = objectives.get(0).getLevel(); - boolean oneLevelOnly = true; - for (Objective objective : objectives) { - if (objective.getLevel() != level) { - oneLevelOnly = false; - break; - } - } - - if (oneLevelOnly) { - leveledObjectives = new Objective[1][objectives.size()]; - for (int i = 0; i < objectives.size(); i++) { - leveledObjectives[0][i] = objectives.get(i); - } - return leveledObjectives; - } - - Objective[] objectivesArray = getSortedByLevelObjectives(objectives); - - int numberOfLevels = getNumberOfObjectiveLevels(objectivesArray); - - leveledObjectives = new Objective[numberOfLevels][]; - - fillLeveledObjectives(objectivesArray); - - return leveledObjectives; - } - - private void fillLeveledObjectives(Objective[] objectivesArray) { - int actLevel = objectivesArray[0].getLevel(); - int levelIndex = 0; - int lastIndex = 0; - int corrigationForLastLevel = 0; - boolean oneObjectiveAtLastLevel = false; - for (int i = 0; i < objectivesArray.length; i++) { - if (i == objectivesArray.length - 1) { - corrigationForLastLevel = 1; - if (objectivesArray[i - 1].getLevel() != objectivesArray[i].getLevel()) { - oneObjectiveAtLastLevel = true; - corrigationForLastLevel = 0; - } - } - if (objectivesArray[i].getLevel() != actLevel || corrigationForLastLevel == 1 || oneObjectiveAtLastLevel) { - leveledObjectives[levelIndex] = new Objective[i - lastIndex + corrigationForLastLevel]; - for (int j = lastIndex; j < i + corrigationForLastLevel; j++) { - leveledObjectives[levelIndex][j - lastIndex] = objectivesArray[j]; - } - if (oneObjectiveAtLastLevel) { - leveledObjectives[levelIndex + 1] = new Objective[1]; - leveledObjectives[levelIndex + 1][0] = objectivesArray[i]; - } - actLevel = objectivesArray[i].getLevel(); - levelIndex++; - lastIndex = i; - } - } - } - - private int getNumberOfObjectiveLevels(Objective[] objectivesArray) { - - int actLevel = objectivesArray[0].getLevel(); - int numberOfLevels = 1; - - for (int i = 1; i < objectivesArray.length; i++) { - if (objectivesArray[i].getLevel() != actLevel) { - numberOfLevels++; - actLevel = objectivesArray[i].getLevel(); - } - } - - return numberOfLevels; - } - - private Objective[] getSortedByLevelObjectives(List objectives) { - Objective[] objectivesArray = objectives.toArray(new Objective[objectives.size()]); - Arrays.sort(objectivesArray, Comparator.comparingInt(Objective::getLevel)); - return objectivesArray; - } - - public Objective[][] getLeveledObjectives() { - return leveledObjectives; - } - -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java index b160b453..df86f36a 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java @@ -97,14 +97,4 @@ public interface Objective { */ boolean satisfiesHardObjective(Double fitness); - /** - * Set the level of the objective. - */ - void setLevel(int level); - - /** - * Gets the level of the objective. - */ - int getLevel(); - } diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java index 23fb24ee..0c925096 100644 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java +++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java @@ -1,218 +1,58 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.query.dse.objectives; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Random; - -import org.eclipse.viatra.query.runtime.matchers.util.Preconditions; - -/** - * This class is responsible to compare and sort fitness values. {@link TrajectoryFitness} instances can be added to an - * instance of this class, that it can sort them. - * - * @author András Szabolcs Nagy - */ -public class ObjectiveComparatorHelper { - - private Objective[][] leveledObjectives; - private List trajectoryFitnesses = new ArrayList(); - private Random random = new Random(); - private boolean computeCrowdingDistance = false; - - public ObjectiveComparatorHelper(Objective[][] leveledObjectives) { - this.leveledObjectives = leveledObjectives; - } - - public void setComputeCrowdingDistance(boolean computeCrowdingDistance) { - this.computeCrowdingDistance = computeCrowdingDistance; - } - - /** - * Compares two fitnesses based on hierarchical dominance. Returns -1 if the second parameter {@code o2} is a better - * solution ({@code o2} dominates {@code o1}), 1 if the first parameter {@code o1} is better ({@code o1} dominates - * {@code o2}) and returns 0 if they are non-dominating each other. - */ - public int compare(Fitness o1, Fitness o2) { - - levelsLoop: - for (Objective[] leveledObjective : leveledObjectives) { - - boolean o1HasBetterFitness = false; - boolean o2HasBetterFitness = false; - - for (Objective objective : leveledObjective) { - String objectiveName = objective.getName(); - int sgn = objective.getComparator().compare(o1.get(objectiveName), o2.get(objectiveName)); - - if (sgn < 0) { - o2HasBetterFitness = true; - } - if (sgn > 0) { - o1HasBetterFitness = true; - } - if (o1HasBetterFitness && o2HasBetterFitness) { - continue levelsLoop; - } - } - if (o2HasBetterFitness) { - return -1; - } else if (o1HasBetterFitness) { - return 1; - } - } - - return 0; - - } - - /** - * Adds a {@link TrajectoryFitness} to an inner list to compare later. - * - * @param trajectoryFitness - */ - public void addTrajectoryFitness(TrajectoryFitness trajectoryFitness) { - trajectoryFitnesses.add(trajectoryFitness); - } - - /** - * Clears the inner {@link TrajectoryFitness} list. - */ - public void clearTrajectoryFitnesses() { - trajectoryFitnesses.clear(); - } - - /** - * Returns the inner {@link TrajectoryFitness} list. - */ - public List getTrajectoryFitnesses() { - return trajectoryFitnesses; - } - - /** - * Returns a random {@link TrajectoryFitness} from the pareto front. - */ - public TrajectoryFitness getRandomBest() { - List paretoFront = getParetoFront(); - int randomIndex = random.nextInt(paretoFront.size()); - return paretoFront.get(randomIndex); - } - - /** - * Returns the pareto front of the previously added {@link TrajectoryFitness}. - */ - public List getParetoFront() { - return getFronts().get(0); - } - - /** - * Returns the previously added {@link TrajectoryFitness} instances in fronts. - */ - public List> getFronts() { - Preconditions.checkArgument(!trajectoryFitnesses.isEmpty(), "No trajectory fitnesses were added."); - List> fronts = new ArrayList>(); - - Map> dominatedInstances = new HashMap>(); - Map dominatingInstances = new HashMap(); - - // calculate dominations - for (TrajectoryFitness TrajectoryFitnessP : trajectoryFitnesses) { - dominatedInstances.put(TrajectoryFitnessP, new ArrayList()); - dominatingInstances.put(TrajectoryFitnessP, 0); - - for (TrajectoryFitness TrajectoryFitnessQ : trajectoryFitnesses) { - int dominates = compare(TrajectoryFitnessP.fitness, TrajectoryFitnessQ.fitness); - if (dominates > 0) { - dominatedInstances.get(TrajectoryFitnessP).add(TrajectoryFitnessQ); - } else if (dominates < 0) { - dominatingInstances.put(TrajectoryFitnessP, dominatingInstances.get(TrajectoryFitnessP) + 1); - } - } - - if (dominatingInstances.get(TrajectoryFitnessP) == 0) { - // p belongs to the first front - TrajectoryFitnessP.rank = 1; - if (fronts.isEmpty()) { - ArrayList firstDominationFront = new ArrayList(); - firstDominationFront.add(TrajectoryFitnessP); - fronts.add(firstDominationFront); - } else { - List firstDominationFront = fronts.get(0); - firstDominationFront.add(TrajectoryFitnessP); - } - } - } - - // create fronts - int i = 1; - while (fronts.size() == i) { - ArrayList nextDominationFront = new ArrayList(); - for (TrajectoryFitness TrajectoryFitnessP : fronts.get(i - 1)) { - for (TrajectoryFitness TrajectoryFitnessQ : dominatedInstances.get(TrajectoryFitnessP)) { - dominatingInstances.put(TrajectoryFitnessQ, dominatingInstances.get(TrajectoryFitnessQ) - 1); - if (dominatingInstances.get(TrajectoryFitnessQ) == 0) { - TrajectoryFitnessQ.rank = i + 1; - nextDominationFront.add(TrajectoryFitnessQ); - } - } - } - i++; - if (!nextDominationFront.isEmpty()) { - if (computeCrowdingDistance) { - crowdingDistanceAssignment(nextDominationFront, leveledObjectives); - } - fronts.add(nextDominationFront); - } - } - - return fronts; - } - - /** - * Executes the crowding distance assignment for the specified front. - * - * @param front - */ - public static void crowdingDistanceAssignment(List front, Objective[][] leveledObjectives) { - - for (TrajectoryFitness InstanceData : front) { - // initialize crowding distance - InstanceData.crowdingDistance = 0; - } - - for (final Objective[] objectives : leveledObjectives) { - for (final Objective objective : objectives) { - - final String m = objective.getName(); - TrajectoryFitness[] sortedFront = front.toArray(new TrajectoryFitness[0]); - // sort using m-th objective value - Arrays.sort(sortedFront, (o1, o2) -> objective.getComparator().compare(o1.fitness.get(m), o2.fitness.get(m))); - // so that boundary points are always selected - sortedFront[0].crowdingDistance = Double.POSITIVE_INFINITY; - sortedFront[sortedFront.length - 1].crowdingDistance = Double.POSITIVE_INFINITY; - // If minimal and maximal fitness value for this objective are - // equal, then do not change crowding distance - if (sortedFront[0].fitness.get(m) != sortedFront[sortedFront.length - 1].fitness.get(m)) { - for (int i = 1; i < sortedFront.length - 1; i++) { - double newCrowdingDistance = sortedFront[i].crowdingDistance; - newCrowdingDistance += (sortedFront[i + 1].fitness.get(m) - sortedFront[i - 1].fitness.get(m)) - / (sortedFront[sortedFront.length - 1].fitness.get(m) - sortedFront[0].fitness.get(m)); - - sortedFront[i].crowdingDistance = newCrowdingDistance; - } - } - } - } - } - -} +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.query.dse.objectives; + +import java.util.List; + +/** + * This class is responsible to compare and sort fitness values. + * + * @author András Szabolcs Nagy + */ +public class ObjectiveComparatorHelper { + + private final List objectives; + + public ObjectiveComparatorHelper(List objectives) { + this.objectives = objectives; + } + + /** + * Compares two fitnesses based on dominance. Returns -1 if the second parameter {@code o2} is a better + * solution ({@code o2} dominates {@code o1}), 1 if the first parameter {@code o1} is better ({@code o1} dominates + * {@code o2}) and returns 0 if they are non-dominating each other. + */ + public int compare(Fitness o1, Fitness o2) { + + boolean o1HasBetterFitness = false; + boolean o2HasBetterFitness = false; + + for (Objective objective : objectives) { + String objectiveName = objective.getName(); + int sgn = objective.getComparator().compare(o1.get(objectiveName), o2.get(objectiveName)); + + if (sgn < 0) { + o2HasBetterFitness = true; + } + if (sgn > 0) { + o1HasBetterFitness = true; + } + if (o1HasBetterFitness && o2HasBetterFitness) { + break; + } + } + if (o2HasBetterFitness) { + } else if (o1HasBetterFitness) { + return 1; + } + + return 0; + + } +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/TrajectoryFitness.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/TrajectoryFitness.java deleted file mode 100644 index b9ff7067..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/TrajectoryFitness.java +++ /dev/null @@ -1,71 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.query.dse.objectives; - -import java.util.Arrays; - - -/** - * This class represents a trajectory and its fitness. - * @author Andras Szabolcs Nagy - * - */ -public class TrajectoryFitness { - - public Object[] trajectory; - public Fitness fitness; - - public int rank; - public double crowdingDistance; - - private int hash; - - public int survive; - - /** - * Creates a {@link TrajectoryFitness} with the full trajectory. - * @param trajectory The trajectory. - * @param fitness The fitness. - */ - public TrajectoryFitness(Object[] trajectory, Fitness fitness) { - this.fitness = fitness; - this.trajectory = trajectory; - } - - /** - * Creates a {@link TrajectoryFitness} with the given activation id} - * @param transition The transition. - * @param fitness The fitness. - */ - public TrajectoryFitness(Object transition, Fitness fitness) { - this.fitness = fitness; - trajectory = new Object[] {transition}; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof TrajectoryFitness) { - return Arrays.equals(trajectory, ((TrajectoryFitness) obj).trajectory); - } - return false; - } - - @Override - public int hashCode() { - if (hash == 0 && trajectory.length > 0) { - hash = Arrays.hashCode(trajectory); - } - return hash; - } - - @Override - public String toString() { - return Arrays.toString(trajectory) + fitness.toString(); - } -} -- cgit v1.2.3-54-g00ecf From 85347a15d62169b233b36754aacd654623f7770c Mon Sep 17 00:00:00 2001 From: nagilooh Date: Wed, 2 Aug 2023 12:07:30 +0200 Subject: Update CRA test case --- .../refinery/store/query/dse/CRAExamplesTest.java | 233 ++++++++++++++++----- 1 file changed, 186 insertions(+), 47 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java index 8fe50a42..2effb353 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java @@ -1,112 +1,140 @@ package tools.refinery.store.query.dse; +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; import tools.refinery.store.query.dse.internal.TransformationRule; +import tools.refinery.store.query.dse.strategy.BestFirstStrategy; +import tools.refinery.store.query.dse.strategy.DepthFirstStrategy; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.internal.FileFormat; import java.util.List; import static tools.refinery.store.query.literal.Literals.not; public class CRAExamplesTest { - private static final Symbol classModel = Symbol.of("ClassModel", 1); + private static final Symbol name = Symbol.of("Name", 1, String.class); + +// private static final Symbol classModel = Symbol.of("ClassModel", 1); private static final Symbol classElement = Symbol.of("ClassElement", 1); - private static final Symbol feature = Symbol.of("Feature", 1); +// private static final Symbol feature = Symbol.of("Feature", 1); + private static final Symbol attribute = Symbol.of("Attribute", 1); + private static final Symbol method = Symbol.of("Method", 1); - private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); +// private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); + private static final Symbol dataDependency = Symbol.of("DataDependency", 2); + private static final Symbol functionalDependency = Symbol.of("FunctionalDependency", 2); private static final Symbol features = Symbol.of("Features", 2); private static final Symbol classes = Symbol.of("Classes", 2); - private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); +// private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); - private static final AnySymbolView featureView = new KeyOnlyView<>(feature); - private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); +// private static final AnySymbolView featureView = new KeyOnlyView<>(feature); + private static final AnySymbolView attributeView = new KeyOnlyView<>(attribute); + private static final AnySymbolView methodView = new KeyOnlyView<>(method); +// private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); + private static final AnySymbolView dataDependencyView = new KeyOnlyView<>(dataDependency); + private static final AnySymbolView functionalDependencyView = new KeyOnlyView<>(functionalDependency); private static final AnySymbolView featuresView = new KeyOnlyView<>(features); private static final AnySymbolView classesView = new KeyOnlyView<>(classes); /*Example Transformation rules*/ + private static final RelationalQuery feature = Query.of("Feature", + (builder, f) -> builder.clause( + attributeView.call(f)) + .clause( + methodView.call(f)) + ); + private static final RelationalQuery assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", - (builder, model, c, f) -> builder.clause( + (builder, c, f) -> builder.clause( classElementView.call(c), - classesView.call(model, c), +// classesView.call(model, c), encapsulatesView.call(c, f) )); - private static final RelationalQuery assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f) - -> builder.clause((model, c1) -> List.of( - classModelView.call(model), - featureView.call(f), - classElementView.call(c2), - featuresView.call(model, f), - classesView.call(model, c1), - not(assignFeaturePreconditionHelper.call(model, c2, f)), - not(encapsulatesView.call(c2, f)) + private static final RelationalQuery assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", + (builder, f, c1) -> builder.clause((c2) -> List.of( +// classModelView.call(model), + feature.call(f), + classElementView.call(c1), +// featuresView.call(model, f), + not(assignFeaturePreconditionHelper.call(c2, f)), + not(encapsulatesView.call(c1, f)) ))); private static final RelationalQuery deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), + (builder, c) -> builder.clause((f) -> List.of( +// classModelView.call(model), classElementView.call(c), - featuresView.call(model, f), +// featuresView.call(model, f), not(encapsulatesView.call(c, f)) ))); private static final RelationalQuery createClassPreconditionHelper = Query.of("CreateClassPreconditionHelper", - (builder, model, f, c) -> builder.clause( + (builder, f, c) -> builder.clause( classElementView.call(c), - classesView.call(model, c), +// classesView.call(model, c), encapsulatesView.call(c, f) )); private static final RelationalQuery createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model, f) -> builder.clause((c) -> List.of( - classModelView.call(model), - featureView.call(f), - not(createClassPreconditionHelper.call(model, f, c)) + (builder, f) -> builder.clause((c) -> List.of( +// classModelView.call(model), + feature.call(f), + not(createClassPreconditionHelper.call(f, c)) ))); - private static final RelationalQuery moveFeature = Query.of("MoveFeature", - (builder, c1, c2, f) -> builder.clause((model) -> List.of( - classModelView.call(model), + private static final RelationalQuery moveFeaturePrecondition = Query.of("MoveFeature", + (builder, c1, c2, f) -> builder.clause( +// classModelView.call(model), classElementView.call(c1), classElementView.call(c2), - featureView.call(f), - classesView.call(model, c1), - classesView.call(model, c2), - featuresView.call(model, f), + c1.notEquivalent(c2), + feature.call(f), +// classesView.call(model, c1), +// classesView.call(model, c2), +// featuresView.call(model, f), encapsulatesView.call(c1, f) - ))); + )); private static final TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", assignFeaturePrecondition, (model) -> { - var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); +// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); + var encapsulatesInterpretation = model.getInterpretation(encapsulates); return ((Tuple activation) -> { var feature = activation.get(0); var classElement = activation.get(1); - isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); +// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); + encapsulatesInterpretation.put(Tuple.of(classElement, feature), true); }); }); private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", deleteEmptyClassPrecondition, (model) -> { - var classesInterpretation = model.getInterpretation(classes); +// var classesInterpretation = model.getInterpretation(classes); var classElementInterpretation = model.getInterpretation(classElement); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); + // TODO: can we move dseAdapter outside? + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); + var classElement = activation.get(0); - classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); classElementInterpretation.put(Tuple.of(classElement), false); dseAdapter.deleteObject(Tuple.of(classElement)); }); @@ -115,21 +143,132 @@ public class CRAExamplesTest { private static final TransformationRule createClassRule = new TransformationRule("CreateClass", createClassPrecondition, (model) -> { - var adapter = model.getAdapter(DesignSpaceExplorationAdapter.class); var classElementInterpretation = model.getInterpretation(classElement); - var classesInterpretation = model.getInterpretation(classes); +// var classesInterpretation = model.getInterpretation(classes); var encapsulatesInterpretation = model.getInterpretation(encapsulates); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); return ((Tuple activation) -> { - var modelElement = activation.get(0); - var feature = activation.get(1); + // TODO: can we move dseAdapter outside? + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); + var feature = activation.get(0); var newClassElement = dseAdapter.createObject(); var newClassElementId = newClassElement.get(0); classElementInterpretation.put(newClassElement, true); - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true); }); }); + private static final TransformationRule moveFeatureRule = new TransformationRule("MoveFeature", + moveFeaturePrecondition, + (model) -> { + var encapsulatesInterpretation = model.getInterpretation(encapsulates); + return ((Tuple activation) -> { + var classElement1 = activation.get(0); + var classElement2 = activation.get(1); + var feature = activation.get(2); + + encapsulatesInterpretation.put(Tuple.of(classElement1, feature), false); + encapsulatesInterpretation.put(Tuple.of(classElement2, feature), true); + }); + }); + + @Test + void craTest() { + var store = ModelStore.builder() + .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, + functionalDependency, name) + .with(ViatraModelQueryAdapter.builder() + .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, + deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, + moveFeaturePrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) +// .strategy(new DepthFirstStrategy(3).continueIfHardObjectivesFulfilled() + .strategy(new BestFirstStrategy(6).continueIfHardObjectivesFulfilled() +// .goOnOnlyIfFitnessIsBetter() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// dseAdapter.setRandom(1); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + +// var modelInterpretation = model.getInterpretation(classModel); + var nameInterpretation = model.getInterpretation(name); + var methodInterpretation = model.getInterpretation(method); + var attributeInterpretation = model.getInterpretation(attribute); + var dataDependencyInterpretation = model.getInterpretation(dataDependency); + var functionalDependencyInterpretation = model.getInterpretation(functionalDependency); + +// var modelElement = dseAdapter.createObject(); + var method1 = dseAdapter.createObject(); + var method1Id = method1.get(0); + var method2 = dseAdapter.createObject(); + var method2Id = method2.get(0); + var method3 = dseAdapter.createObject(); + var method3Id = method3.get(0); + var method4 = dseAdapter.createObject(); + var method4Id = method4.get(0); + var attribute1 = dseAdapter.createObject(); + var attribute1Id = attribute1.get(0); + var attribute2 = dseAdapter.createObject(); + var attribute2Id = attribute2.get(0); + var attribute3 = dseAdapter.createObject(); + var attribute3Id = attribute3.get(0); + var attribute4 = dseAdapter.createObject(); + var attribute4Id = attribute4.get(0); + var attribute5 = dseAdapter.createObject(); + var attribute5Id = attribute5.get(0); + + nameInterpretation.put(method1, "M1"); + nameInterpretation.put(method2, "M2"); + nameInterpretation.put(method3, "M3"); + nameInterpretation.put(method4, "M4"); + nameInterpretation.put(attribute1, "A1"); + nameInterpretation.put(attribute2, "A2"); + nameInterpretation.put(attribute3, "A3"); + nameInterpretation.put(attribute4, "A4"); + nameInterpretation.put(attribute5, "A5"); + + + +// modelInterpretation.put(modelElement, true); + methodInterpretation.put(method1, true); + methodInterpretation.put(method2, true); + methodInterpretation.put(method3, true); + methodInterpretation.put(method4, true); + attributeInterpretation.put(attribute1, true); + attributeInterpretation.put(attribute2, true); + attributeInterpretation.put(attribute3, true); + attributeInterpretation.put(attribute4, true); + attributeInterpretation.put(attribute5, true); + + dataDependencyInterpretation.put(Tuple.of(method1Id, attribute1Id), true); + dataDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); + dataDependencyInterpretation.put(Tuple.of(method2Id, attribute2Id), true); + dataDependencyInterpretation.put(Tuple.of(method3Id, attribute3Id), true); + dataDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); + dataDependencyInterpretation.put(Tuple.of(method4Id, attribute3Id), true); + dataDependencyInterpretation.put(Tuple.of(method4Id, attribute5Id), true); + + functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); + functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute4Id), true); + functionalDependencyInterpretation.put(Tuple.of(method2Id, attribute1Id), true); + functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute1Id), true); + functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); + functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true); + + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + System.out.println("states size: " + states.size()); + System.out.println("states: " + states); + var visualizer = model.getAdapter(ModelVisualizerAdapter.class); + visualizer.renderDesignSpace("test_output", FileFormat.SVG); + } + } -- cgit v1.2.3-54-g00ecf From c93d455d2459c2fbe0cfce7693d592986f65d44c Mon Sep 17 00:00:00 2001 From: nagilooh Date: Wed, 2 Aug 2023 14:07:35 +0200 Subject: Move DSE to new subproject --- settings.gradle.kts | 1 + subprojects/store-dse/build.gradle.kts | 16 + .../tools/refinery/store/dse/ActionFactory.java | 9 + .../store/dse/DesignSpaceExplorationAdapter.java | 61 +++ .../store/dse/DesignSpaceExplorationBuilder.java | 43 ++ .../dse/DesignSpaceExplorationStoreAdapter.java | 6 + .../java/tools/refinery/store/dse/Strategy.java | 8 + .../refinery/store/dse/internal/Activation.java | 9 + .../DesignSpaceExplorationAdapterImpl.java | 283 ++++++++++++ .../DesignSpaceExplorationBuilderImpl.java | 62 +++ .../DesignSpaceExplorationStoreAdapterImpl.java | 57 +++ .../store/dse/internal/TransformationRule.java | 84 ++++ .../AlwaysSatisfiedDummyHardObjective.java | 51 +++ .../store/dse/objectives/BaseObjective.java | 131 ++++++ .../refinery/store/dse/objectives/Comparators.java | 25 ++ .../refinery/store/dse/objectives/Fitness.java | 21 + .../refinery/store/dse/objectives/Objective.java | 100 +++++ .../dse/objectives/ObjectiveComparatorHelper.java | 58 +++ .../store/dse/strategy/BestFirstStrategy.java | 190 ++++++++ .../store/dse/strategy/DepthFirstStrategy.java | 108 +++++ .../tools/refinery/store/dse/CRAExamplesTest.java | 274 ++++++++++++ .../java/tools/refinery/store/dse/DebugTest.java | 117 +++++ .../store/dse/DesignSpaceExplorationTest.java | 487 +++++++++++++++++++++ .../refinery/store/dse/TransformationRuleTest.java | 403 +++++++++++++++++ .../refinery/store/dse/tests/QueryAssertions.java | 57 +++ .../refinery/store/query/dse/ActionFactory.java | 9 - .../query/dse/DesignSpaceExplorationAdapter.java | 61 --- .../query/dse/DesignSpaceExplorationBuilder.java | 44 -- .../dse/DesignSpaceExplorationStoreAdapter.java | 6 - .../tools/refinery/store/query/dse/Strategy.java | 10 - .../store/query/dse/internal/Activation.java | 9 - .../DesignSpaceExplorationAdapterImpl.java | 283 ------------ .../DesignSpaceExplorationBuilderImpl.java | 62 --- .../DesignSpaceExplorationStoreAdapterImpl.java | 57 --- .../query/dse/internal/TransformationRule.java | 84 ---- .../AlwaysSatisfiedDummyHardObjective.java | 51 --- .../store/query/dse/objectives/BaseObjective.java | 131 ------ .../store/query/dse/objectives/Comparators.java | 25 -- .../store/query/dse/objectives/Fitness.java | 21 - .../store/query/dse/objectives/Objective.java | 100 ----- .../dse/objectives/ObjectiveComparatorHelper.java | 58 --- .../query/dse/strategy/BestFirstStrategy.java | 190 -------- .../query/dse/strategy/DepthFirstStrategy.java | 108 ----- .../refinery/store/query/dse/CRAExamplesTest.java | 274 ------------ .../tools/refinery/store/query/dse/DebugTest.java | 117 ----- .../query/dse/DesignSpaceExplorationTest.java | 487 --------------------- .../store/query/dse/TransformationRuleTest.java | 403 ----------------- 47 files changed, 2661 insertions(+), 2590 deletions(-) create mode 100644 subprojects/store-dse/build.gradle.kts create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/ActionFactory.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationStoreAdapter.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/Strategy.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/Activation.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/AlwaysSatisfiedDummyHardObjective.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Comparators.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Fitness.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/BestFirstStrategy.java delete mode 100644 subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/DepthFirstStrategy.java delete mode 100644 subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java delete mode 100644 subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java delete mode 100644 subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java delete mode 100644 subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/TransformationRuleTest.java (limited to 'subprojects') diff --git a/settings.gradle.kts b/settings.gradle.kts index 8b53e7f7..0eae2800 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -14,6 +14,7 @@ include( "language-semantics", "language-web", "store", + "store-dse", "store-query", "store-query-viatra", "store-reasoning", diff --git a/subprojects/store-dse/build.gradle.kts b/subprojects/store-dse/build.gradle.kts new file mode 100644 index 00000000..bb1dee7d --- /dev/null +++ b/subprojects/store-dse/build.gradle.kts @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ + +plugins { + id("tools.refinery.gradle.java-library") +} + +dependencies { + api(project(":refinery-store-query")) + api(project(":refinery-store-query-viatra")) + api(project(":refinery-store-reasoning")) + api(project(":refinery-visualization")) +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java new file mode 100644 index 00000000..2af22963 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java @@ -0,0 +1,9 @@ +package tools.refinery.store.dse; + +import org.eclipse.collections.api.block.procedure.Procedure; +import tools.refinery.store.model.Model; +import tools.refinery.store.tuple.Tuple; + +public interface ActionFactory { + Procedure prepare(Model model); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java new file mode 100644 index 00000000..729a6fc9 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java @@ -0,0 +1,61 @@ +package tools.refinery.store.dse; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.map.Version; +import tools.refinery.store.dse.internal.Activation; +import tools.refinery.store.dse.internal.DesignSpaceExplorationBuilderImpl; +import tools.refinery.store.dse.objectives.Fitness; +import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; + +import java.util.Collection; +import java.util.List; +import java.util.Random; + +public interface DesignSpaceExplorationAdapter extends ModelAdapter { + @Override + DesignSpaceExplorationStoreAdapter getStoreAdapter(); + + static DesignSpaceExplorationBuilder builder() { + return new DesignSpaceExplorationBuilderImpl(); + } + + Collection explore(); + + public int getModelSize(); + + public Tuple1 createObject(); + + public Tuple deleteObject(Tuple tuple); + + public boolean checkGlobalConstraints(); + + public boolean backtrack(); + + public Fitness calculateFitness(); + + public void newSolution(); + + public int getDepth(); + + public Collection getUntraversedActivations(); + + public boolean fireActivation(Activation activation); + + public void fireRandomActivation(); + + public boolean isCurrentInTrajectory(); + + public List getTrajectory(); + + public boolean isCurrentStateAlreadyTraversed(); + + public ObjectiveComparatorHelper getObjectiveComparatorHelper(); + + public void restoreTrajectory(List trajectory); + + public void setRandom(Random random); + + public void setRandom(long seed); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java new file mode 100644 index 00000000..8ca0037d --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java @@ -0,0 +1,43 @@ +package tools.refinery.store.dse; + +import tools.refinery.store.adapter.ModelAdapterBuilder; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.objectives.Objective; + +import java.util.Collection; +import java.util.List; + +public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { + default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRules) { + return transformations(List.of(transformationRules)); + } + + default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { + transformationRules.forEach(this::transformation); + return this; + } + + default DesignSpaceExplorationBuilder globalConstraints(RelationalQuery... globalConstraints) { + return globalConstraints(List.of(globalConstraints)); + } + + default DesignSpaceExplorationBuilder globalConstraints(Collection globalConstraints) { + globalConstraints.forEach(this::globalConstraint); + return this; + } + + default DesignSpaceExplorationBuilder objectives(Objective... objectives) { + return objectives(List.of(objectives)); + } + + default DesignSpaceExplorationBuilder objectives(Collection objectives) { + objectives.forEach(this::objective); + return this; + } + + DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule); + DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint); + DesignSpaceExplorationBuilder objective(Objective objective); + DesignSpaceExplorationBuilder strategy(Strategy strategy); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java new file mode 100644 index 00000000..5964cd82 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java @@ -0,0 +1,6 @@ +package tools.refinery.store.dse; + +import tools.refinery.store.adapter.ModelStoreAdapter; + +public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter { +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java new file mode 100644 index 00000000..cef43386 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java @@ -0,0 +1,8 @@ +package tools.refinery.store.dse; + +public interface Strategy { + + public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter); + + public void explore(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java new file mode 100644 index 00000000..f1de00e6 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java @@ -0,0 +1,9 @@ +package tools.refinery.store.dse.internal; + +import tools.refinery.store.tuple.Tuple; + +public record Activation(TransformationRule transformationRule, Tuple activation) { + public boolean fire() { + return transformationRule.fireActivation(activation); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java new file mode 100644 index 00000000..5fb54da9 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -0,0 +1,283 @@ +package tools.refinery.store.dse.internal; + +import tools.refinery.store.map.Version; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.dse.DesignSpaceExplorationAdapter; +import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter; +import tools.refinery.store.dse.Strategy; +import tools.refinery.store.dse.objectives.Fitness; +import tools.refinery.store.dse.objectives.Objective; +import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper; +import tools.refinery.store.query.resultset.ResultSet; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; +import tools.refinery.visualization.ModelVisualizerAdapter; + +import java.util.*; + +public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExplorationAdapter { + static final Symbol NODE_COUNT_SYMBOL = Symbol.of("MODEL_SIZE", 0, Integer.class, 0); + private final Model model; + private final ModelQueryAdapter queryEngine; + private final DesignSpaceExplorationStoreAdapterImpl storeAdapter; + private final LinkedHashSet transformationRules; + private final LinkedHashSet globalConstraints; + private final List objectives; + private final LinkedHashSet> globalConstraintResultSets = new LinkedHashSet<>(); + private final Interpretation sizeInterpretation; + private final Strategy strategy; + + private ObjectiveComparatorHelper objectiveComparatorHelper; + private List trajectory = new LinkedList<>(); + private Fitness lastFitness; + private final LinkedHashSet solutions = new LinkedHashSet<>(); + private Map> statesAndUntraversedActivations; + private Map> statesAndTraversedActivations; + private Random random = new Random(); + private boolean isNewState = false; + private final boolean isVisualizationEnabled; + private final ModelVisualizerAdapter modelVisualizerAdapter; + + public List getTrajectory() { + return new LinkedList<>(trajectory); + } + + public DesignSpaceExplorationAdapterImpl(Model model, DesignSpaceExplorationStoreAdapterImpl storeAdapter) { + this.model = model; + this.storeAdapter = storeAdapter; + this.sizeInterpretation = model.getInterpretation(NODE_COUNT_SYMBOL); + queryEngine = model.getAdapter(ModelQueryAdapter.class); + + globalConstraints = storeAdapter.getGlobalConstraints(); + for (var constraint : globalConstraints) { + globalConstraintResultSets.add(queryEngine.getResultSet(constraint)); + } + + transformationRules = storeAdapter.getTransformationSpecifications(); + for (var rule : transformationRules) { + rule.prepare(model, queryEngine); + } + + objectives = storeAdapter.getObjectives(); + statesAndUntraversedActivations = new HashMap<>(); + statesAndTraversedActivations = new HashMap<>(); + strategy = storeAdapter.getStrategy(); + modelVisualizerAdapter = model.tryGetAdapter(ModelVisualizerAdapter.class).orElse(null); + isVisualizationEnabled = modelVisualizerAdapter != null; + + } + + @Override + public Model getModel() { + return model; + } + + @Override + public DesignSpaceExplorationStoreAdapter getStoreAdapter() { + return storeAdapter; + } + + @Override + public LinkedHashSet explore() { + var state = model.commit(); + trajectory.add(state); + statesAndUntraversedActivations.put(state, getAllActivations()); + statesAndTraversedActivations.put(state, new LinkedHashSet<>()); + strategy.initStrategy(this); + strategy.explore(); + return solutions; + } + + @Override + public int getModelSize() { + return sizeInterpretation.get(Tuple.of()); + } + + @Override + public Tuple1 createObject() { + var newNodeId = getModelSize(); + sizeInterpretation.put(Tuple.of(), newNodeId + 1); + return Tuple.of(newNodeId); + } + + @Override + public Tuple deleteObject(Tuple tuple) { + if (tuple.getSize() != 1) { + throw new IllegalArgumentException("Tuple size must be 1"); + } +// TODO: implement more efficient deletion +// if (tuple.get(0) == getModelSize() - 1) { +// sizeInterpretation.put(Tuple.of(), getModelSize() - 1); +// } + return tuple; + } + + @Override + public boolean checkGlobalConstraints() { + for (var resultSet : globalConstraintResultSets) { + if (resultSet.size() > 0) { + return false; + } + } + return true; + } + + @Override + public boolean backtrack() { + if (trajectory.size() < 2) { + return false; + } + if (isVisualizationEnabled) { + modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 1), + trajectory.get(trajectory.size() - 2), "backtrack"); + } + model.restore(trajectory.get(trajectory.size() - 2)); + trajectory.remove(trajectory.size() - 1); + return true; + } + + @Override + public void restoreTrajectory(List trajectory) { + model.restore(trajectory.get(trajectory.size() - 1)); +// if (isVisualizationEnabled) { +// modelVisualizerAdapter.addTransition(this.trajectory.get(trajectory.size() - 1), +// trajectory.get(trajectory.size() - 1), "restore"); +// } + this.trajectory = trajectory; + + } + + @Override + public void setRandom(Random random) { + this.random = random; + } + + @Override + public void setRandom(long seed) { + this.random = new Random(seed); + } + + @Override + public Fitness calculateFitness() { + Fitness result = new Fitness(); + boolean satisfiesHardObjectives = true; + for (Objective objective : objectives) { + var fitness = objective.getFitness(this); + result.put(objective.getName(), fitness); + if (objective.isHardObjective() && !objective.satisfiesHardObjective(fitness)) { + satisfiesHardObjectives = false; + } + } + result.setSatisfiesHardObjectives(satisfiesHardObjectives); + + lastFitness = result; + + return result; + } + + @Override + public void newSolution() { + var state = model.getState(); + solutions.add(state); + if (isVisualizationEnabled) { + modelVisualizerAdapter.addSolution(state); + } + } + + @Override + public int getDepth() { + return trajectory.size() - 1; + } + + public LinkedHashSet getUntraversedActivations() { +// return statesAndUntraversedActivations.get(model.getState()); + LinkedHashSet untraversedActivations = new LinkedHashSet<>(); + for (Activation activation : getAllActivations()) { + if (!statesAndTraversedActivations.get(model.getState()).contains(activation)) { + untraversedActivations.add(activation); + } + } + + return untraversedActivations; + } + + @Override + public boolean fireActivation(Activation activation) { + if (activation == null) { + return false; + } + var previousState = model.getState(); + if (!statesAndUntraversedActivations.get(previousState).contains(activation)) { +// TODO: throw exception? + return false; + } + if (!activation.fire()) { + return false; + } + statesAndUntraversedActivations.get(previousState).remove(activation); + statesAndTraversedActivations.get(previousState).add(activation); + var newState = model.commit(); + trajectory.add(newState); + isNewState = !statesAndUntraversedActivations.containsKey(newState); + statesAndUntraversedActivations.put(newState, getAllActivations()); + statesAndTraversedActivations.put(newState, new LinkedHashSet<>()); + if (isVisualizationEnabled) { + if (isNewState) { + modelVisualizerAdapter.addState(newState); + } + modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 2), + trajectory.get(trajectory.size() - 1), activation.transformationRule().getName(), + activation.activation()); + } + return true; + } + + @Override + public void fireRandomActivation() { + var activations = getUntraversedActivations(); + if (activations.isEmpty()) { +// TODO: throw exception + return; + } + int index = random.nextInt(activations.size()); + var iterator = activations.iterator(); + while (index-- > 0) { + iterator.next(); + } + var activationId = iterator.next(); + fireActivation(activationId); + } + + @Override + public boolean isCurrentInTrajectory() { + return trajectory.contains(model.getState()); + } + + public LinkedHashSet getAllActivations() { + LinkedHashSet result = new LinkedHashSet<>(); + for (var rule : transformationRules) { + result.addAll(rule.getAllActivations()); + } + return result; + } + + public boolean isCurrentStateAlreadyTraversed() { +// TODO: check isomorphism? + return !isNewState; + } + + public Fitness getLastFitness() { + return lastFitness; + } + + public ObjectiveComparatorHelper getObjectiveComparatorHelper() { + if (objectiveComparatorHelper == null) { + objectiveComparatorHelper = new ObjectiveComparatorHelper(objectives); + } + return objectiveComparatorHelper; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java new file mode 100644 index 00000000..03508adc --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java @@ -0,0 +1,62 @@ +package tools.refinery.store.dse.internal; + +import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.dse.DesignSpaceExplorationBuilder; +import tools.refinery.store.dse.Strategy; +import tools.refinery.store.dse.objectives.Objective; + +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; + +public class DesignSpaceExplorationBuilderImpl + extends AbstractModelAdapterBuilder + implements DesignSpaceExplorationBuilder { + private final LinkedHashSet transformationSpecifications = new LinkedHashSet<>(); + private final LinkedHashSet globalConstraints = new LinkedHashSet<>(); + private final List objectives = new LinkedList<>(); + private Strategy strategy; + + @Override + protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) { + return new DesignSpaceExplorationStoreAdapterImpl(store, transformationSpecifications, globalConstraints, + objectives, strategy); + } + + @Override + public DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule) { + checkNotConfigured(); + transformationSpecifications.add(transformationRule); + return this; + } + + @Override + public DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint) { + checkNotConfigured(); + globalConstraints.add(globalConstraint); + return this; + } + + @Override + public DesignSpaceExplorationBuilder objective(Objective objective) { + checkNotConfigured(); + objectives.add(objective); + return this; + } + + @Override + public DesignSpaceExplorationBuilder strategy(Strategy strategy) { + checkNotConfigured(); + this.strategy = strategy; + return this; + } + + @Override + protected void doConfigure(ModelStoreBuilder storeBuilder) { + storeBuilder.symbols(DesignSpaceExplorationAdapterImpl.NODE_COUNT_SYMBOL); + super.doConfigure(storeBuilder); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java new file mode 100644 index 00000000..b06462ce --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java @@ -0,0 +1,57 @@ +package tools.refinery.store.dse.internal; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter; +import tools.refinery.store.dse.Strategy; +import tools.refinery.store.dse.objectives.Objective; + +import java.util.LinkedHashSet; +import java.util.List; + +public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { + private final ModelStore store; + private final LinkedHashSet transformationSpecifications; + private final LinkedHashSet globalConstraints; + private final List objectives; + private final Strategy strategy; + + public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, + LinkedHashSet transformationSpecifications, + LinkedHashSet globalConstraints, + List objectives, Strategy strategy) { + this.store = store; + this.transformationSpecifications = transformationSpecifications; + this.globalConstraints = globalConstraints; + this.objectives = objectives; + this.strategy = strategy; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public ModelAdapter createModelAdapter(Model model) { + return new DesignSpaceExplorationAdapterImpl(model, this); + } + + public LinkedHashSet getTransformationSpecifications() { + return transformationSpecifications; + } + + public LinkedHashSet getGlobalConstraints() { + return globalConstraints; + } + + public List getObjectives() { + return objectives; + } + + public Strategy getStrategy() { + return strategy; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java new file mode 100644 index 00000000..ed2e77f1 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java @@ -0,0 +1,84 @@ +package tools.refinery.store.dse.internal; + +import org.eclipse.collections.api.block.procedure.Procedure; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.dse.ActionFactory; +import tools.refinery.store.query.resultset.OrderedResultSet; +import tools.refinery.store.query.resultset.ResultSet; +import tools.refinery.store.tuple.Tuple; + +import java.util.LinkedHashSet; +import java.util.Random; + +public class TransformationRule { + + private final String name; + private final RelationalQuery precondition; + private final ActionFactory actionFactory; + private Procedure action; + private OrderedResultSet activations; + private Random random; + private ModelQueryAdapter queryEngine; + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) { + this(name, precondition, actionFactory, new Random()); + } + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) { + this(name, precondition, actionFactory, new Random(seed)); + } + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) { + this.name = name; + this.precondition = precondition; + this.actionFactory = actionFactory; + this.random = random; + } + public boolean prepare(Model model, ModelQueryAdapter queryEngine) { + action = actionFactory.prepare(model); + this.queryEngine = queryEngine; + activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); + return true; + } + + public boolean fireActivation(Tuple activation) { + action.accept(activation); + queryEngine.flushChanges(); + return true; + } + + public boolean fireRandomActivation() { + return getRandomActivation().fire(); + } + + public String getName() { + return name; + } + + public RelationalQuery getPrecondition() { + return precondition; + } + + public ResultSet getAllActivationsAsSets() { + return activations; + } + + public LinkedHashSet getAllActivations() { + var result = new LinkedHashSet(); + var cursor = activations.getAll(); + while (cursor.move()) { + result.add(new Activation(this, cursor.getKey())); + } + return result; + } + + public Activation getRandomActivation() { + return new Activation(this, activations.getKey(random.nextInt(activations.size()))); + } + + public Activation getActivation(int index) { + return new Activation(this, activations.getKey(index)); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java new file mode 100644 index 00000000..82695704 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.dse.objectives; + +import tools.refinery.store.dse.DesignSpaceExplorationAdapter; + +/** + * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution. + * + * @author Andras Szabolcs Nagy + * + */ +public class AlwaysSatisfiedDummyHardObjective extends BaseObjective { + + private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective"; + + public AlwaysSatisfiedDummyHardObjective() { + super(DEFAULT_NAME); + } + + public AlwaysSatisfiedDummyHardObjective(String name) { + super(name); + } + + @Override + public Double getFitness(DesignSpaceExplorationAdapter context) { + return 0d; + } + + @Override + public boolean isHardObjective() { + return true; + } + + @Override + public boolean satisfiesHardObjective(Double fitness) { + return true; + } + + @Override + public Objective createNew() { + return this; + } + +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java new file mode 100644 index 00000000..24e3280d --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.dse.objectives; + +import tools.refinery.store.dse.DesignSpaceExplorationAdapter; + +import java.util.Comparator; +import java.util.Objects; + +/** + * This abstract class implements the basic functionality of an objective ({@link Objective} namely its name, + * comparator, level and fitness hard constraint. + * + * @author Andras Szabolcs Nagy + * + */ +public abstract class BaseObjective implements Objective { + + protected final String name; + protected Comparator comparator = Comparators.HIGHER_IS_BETTER; + + protected double fitnessConstraint; + protected boolean isThereFitnessConstraint = false; + protected Comparator fitnessConstraintComparator; + + public BaseObjective(String name) { + Objects.requireNonNull(name, "Name of the objective cannot be null."); + this.name = name; + } + + @Override + public String getName() { + return name; + } + + @Override + public void setComparator(Comparator comparator) { + this.comparator = comparator; + } + + @Override + public Comparator getComparator() { + return comparator; + } + + public BaseObjective withComparator(Comparator comparator) { + setComparator(comparator); + return this; + } + + /** + * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the + * current state as a solution. + * + * @param fitnessConstraint + * Solutions should be better than this value. + * @param fitnessConstraintComparator + * {@link Comparator} to determine if the current state is better than the given value. + * @return The actual instance to enable builder pattern like usage. + */ + public BaseObjective withHardConstraintOnFitness(double fitnessConstraint, + Comparator fitnessConstraintComparator) { + this.fitnessConstraint = fitnessConstraint; + this.fitnessConstraintComparator = fitnessConstraintComparator; + this.isThereFitnessConstraint = true; + return this; + } + + /** + * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the + * current state as a solution. The provided comparator will be used. + * + * @param fitnessConstraint + * Solutions should be better than this value. + * @return The actual instance to enable builder pattern like usage. + */ + public BaseObjective withHardConstraintOnFitness(double fitnessConstraint) { + return withHardConstraintOnFitness(fitnessConstraint, null); + } + + @Override + public void init(DesignSpaceExplorationAdapter context) { + if (fitnessConstraintComparator == null) { + fitnessConstraintComparator = comparator; + } + } + + @Override + public boolean isHardObjective() { + return isThereFitnessConstraint; + } + + @Override + public boolean satisfiesHardObjective(Double fitness) { + if (isThereFitnessConstraint) { + int compare = fitnessConstraintComparator.compare(fitness, fitnessConstraint); + if (compare < 0) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof BaseObjective baseObjective) { + return name.equals(baseObjective.getName()); + } + return false; + } + + @Override + public String toString() { + return name; + } + +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java new file mode 100644 index 00000000..e64e04e8 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.dse.objectives; + +import java.util.Comparator; + +public class Comparators { + + private Comparators() { /*Utility class constructor*/ } + + public static final Comparator HIGHER_IS_BETTER = (o1, o2) -> o1.compareTo(o2); + + public static final Comparator LOWER_IS_BETTER = (o1, o2) -> o2.compareTo(o1); + + private static final Double ZERO = (double) 0; + + public static final Comparator DIFFERENCE_TO_ZERO_IS_BETTER = (o1, o2) -> ZERO.compareTo(Math.abs(o1)-Math.abs(o2)); + +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java new file mode 100644 index 00000000..16caed85 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java @@ -0,0 +1,21 @@ +package tools.refinery.store.dse.objectives; + +import java.util.HashMap; + +public class Fitness extends HashMap { + + private boolean satisfiesHardObjectives; + + public boolean isSatisfiesHardObjectives() { + return satisfiesHardObjectives; + } + + public void setSatisfiesHardObjectives(boolean satisfiesHardObjectives) { + this.satisfiesHardObjectives = satisfiesHardObjectives; + } + + @Override + public String toString() { + return super.toString() + " hardObjectives=" + satisfiesHardObjectives; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java new file mode 100644 index 00000000..4e14c9a3 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.dse.objectives; + +import tools.refinery.store.dse.DesignSpaceExplorationAdapter; + +import java.util.Comparator; + +/** + * + * Implementation of this interface represents a single objective of the DSE problem, which can assess a solution + * (trajectory) in a single number. It has a name and a comparator which orders two solution based on the calculated + * value. + *

+ * Objectives can be either hard or soft objectives. Hard objectives can be satisfied or unsatisfied. If all of the hard + * objectives are satisfied on a single solution, then it is considered to be a valid (or goal) solution. + *

+ * Certain objectives can have inner state for calculating the fitness value. In this case a new instance is necessary + * for every new thread, and the {@code createNew} method should not return the same instance more than once. + * + * @author Andras Szabolcs Nagy + * + */ +public interface Objective { + + /** + * Returns the name of the objective. + * + * @return The name of the objective. + */ + String getName(); + + /** + * Sets the {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is to + * minimize or maximize (or minimize or maximize a delta from a given number). + * + * @param comparator The comparator. + */ + void setComparator(Comparator comparator); + + /** + * Returns a {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is + * to minimize or maximize (or minimize or maximize a delta from a given number). + * + * @return The comparator. + */ + Comparator getComparator(); + + /** + * Calculates the value of the objective on a given solution (trajectory). + * + * @param context + * The {@link DesignSpaceExplorationAdapter} + * @return The objective value in double. + */ + Double getFitness(DesignSpaceExplorationAdapter context); + + /** + * Initializes the objective. It is called exactly once for every thread starts. + * + * @param context + * The {@link DesignSpaceExplorationAdapter}. + */ + void init(DesignSpaceExplorationAdapter context); + + /** + * Returns an instance of the {@link Objective}. If it returns the same instance, all the methods has to be thread + * save as they are called concurrently. + * + * @return An instance of the objective. + */ + Objective createNew(); + + /** + * Returns true if the objective is a hard objective. In such a case the method + * {@link Objective#satisfiesHardObjective(Double)} is called. + * + * @return True if the objective is a hard objective. + * @see Objective#satisfiesHardObjective(Double) + * @see Objective + */ + boolean isHardObjective(); + + /** + * Determines if the given fitness value satisfies the hard objective. + * + * @param fitness + * The fitness value of a solution. + * @return True if it satisfies the hard objective or it is a soft constraint. + * @see Objective + */ + boolean satisfiesHardObjective(Double fitness); + +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java new file mode 100644 index 00000000..3184b8c4 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.dse.objectives; + +import java.util.List; + +/** + * This class is responsible to compare and sort fitness values. + * + * @author András Szabolcs Nagy + */ +public class ObjectiveComparatorHelper { + + private final List objectives; + + public ObjectiveComparatorHelper(List objectives) { + this.objectives = objectives; + } + + /** + * Compares two fitnesses based on dominance. Returns -1 if the second parameter {@code o2} is a better + * solution ({@code o2} dominates {@code o1}), 1 if the first parameter {@code o1} is better ({@code o1} dominates + * {@code o2}) and returns 0 if they are non-dominating each other. + */ + public int compare(Fitness o1, Fitness o2) { + + boolean o1HasBetterFitness = false; + boolean o2HasBetterFitness = false; + + for (Objective objective : objectives) { + String objectiveName = objective.getName(); + int sgn = objective.getComparator().compare(o1.get(objectiveName), o2.get(objectiveName)); + + if (sgn < 0) { + o2HasBetterFitness = true; + } + if (sgn > 0) { + o1HasBetterFitness = true; + } + if (o1HasBetterFitness && o2HasBetterFitness) { + break; + } + } + if (o2HasBetterFitness) { + } else if (o1HasBetterFitness) { + return 1; + } + + return 0; + + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java new file mode 100644 index 00000000..05cc5bac --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java @@ -0,0 +1,190 @@ +package tools.refinery.store.dse.strategy; + +import tools.refinery.store.map.Version; +import tools.refinery.store.dse.DesignSpaceExplorationAdapter; +import tools.refinery.store.dse.Strategy; +import tools.refinery.store.dse.internal.Activation; +import tools.refinery.store.dse.objectives.Fitness; +import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.PriorityQueue; + +public class BestFirstStrategy implements Strategy { + + private DesignSpaceExplorationAdapter dseAdapter; + + private int maxDepth; + private boolean backTrackIfSolution = true; + private boolean onlyBetterFirst = false; + + private PriorityQueue trajectoriesToExplore; + + private static class TrajectoryWithFitness { + + public List trajectory; + public Fitness fitness; + + public TrajectoryWithFitness(List trajectory, Fitness fitness) { + super(); + this.trajectory = trajectory; + this.fitness = fitness; + } + + @Override + public String toString() { + return trajectory.toString() + fitness.toString(); + } + + } + + public BestFirstStrategy() { + this(-1); + } + + public BestFirstStrategy(int maxDepth) { + if (maxDepth < 0) { + this.maxDepth = Integer.MAX_VALUE; + } else { + this.maxDepth = maxDepth; + } + } + + public BestFirstStrategy continueIfHardObjectivesFulfilled() { + backTrackIfSolution = false; + return this; + } + + public BestFirstStrategy goOnOnlyIfFitnessIsBetter() { + onlyBetterFirst = true; + return this; + } + + @Override + public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { + this.dseAdapter = designSpaceExplorationAdapter; + final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); + + trajectoriesToExplore = new PriorityQueue(11, + (o1, o2) -> objectiveComparatorHelper.compare(o2.fitness, o1.fitness)); + } + + @Override + public void explore() { + final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); + + boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + // "Global constraint is not satisfied in the first state. Terminate."); + return; + } + + final Fitness firstFitness = dseAdapter.calculateFitness(); + if (firstFitness.isSatisfiesHardObjectives()) { + dseAdapter.newSolution(); + // "First state is a solution. Terminate."); + if (backTrackIfSolution) { + return; + } + } + + if (maxDepth == 0) { + return; + } + + final List firstTrajectory = dseAdapter.getTrajectory(); + TrajectoryWithFitness currentTrajectoryWithFitness = new TrajectoryWithFitness(firstTrajectory, firstFitness); + trajectoriesToExplore.add(currentTrajectoryWithFitness); + + mainLoop: while (true) { + + if (currentTrajectoryWithFitness == null) { + if (trajectoriesToExplore.isEmpty()) { + // "State space is fully traversed."); + return; + } else { + currentTrajectoryWithFitness = trajectoriesToExplore.element(); +// if (logger.isDebugEnabled()) { +// "New trajectory is chosen: " + currentTrajectoryWithFitness); +// } + dseAdapter.restoreTrajectory(currentTrajectoryWithFitness.trajectory); + } + } + + Collection activations = dseAdapter.getUntraversedActivations(); + Iterator iterator = activations.iterator(); + + + + while (iterator.hasNext()) { + final Activation nextActivation = iterator.next(); + if (!iterator.hasNext()) { + // "Last untraversed activation of the state."); + trajectoriesToExplore.remove(currentTrajectoryWithFitness); + } + +// if (logger.isDebugEnabled()) { +// "Executing new activation: " + nextActivation); +// } + dseAdapter.fireActivation(nextActivation); + if (dseAdapter.isCurrentStateAlreadyTraversed()) { + // "The new state is already visited."); + dseAdapter.backtrack(); + } else if (!dseAdapter.checkGlobalConstraints()) { + // "Global constraint is not satisfied."); + dseAdapter.backtrack(); + } else { + final Fitness nextFitness = dseAdapter.calculateFitness(); + if (nextFitness.isSatisfiesHardObjectives()) { + dseAdapter.newSolution(); + // "Found a solution."); + if (backTrackIfSolution) { + dseAdapter.backtrack(); + continue; + } + } + if (dseAdapter.getDepth() >= maxDepth) { + // "Reached max depth."); + dseAdapter.backtrack(); + continue; + } + + TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness( + dseAdapter.getTrajectory(), nextFitness); + trajectoriesToExplore.add(nextTrajectoryWithFitness); + + int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness, + nextTrajectoryWithFitness.fitness); + if (compare < 0) { + // "Better fitness, moving on: " + nextFitness); + currentTrajectoryWithFitness = nextTrajectoryWithFitness; + continue mainLoop; + } else if (compare == 0) { + if (onlyBetterFirst) { + // "Equally good fitness, backtrack: " + nextFitness); + dseAdapter.backtrack(); + continue; + } else { + // "Equally good fitness, moving on: " + nextFitness); + currentTrajectoryWithFitness = nextTrajectoryWithFitness; + continue mainLoop; + } + } else { + // "Worse fitness."); + currentTrajectoryWithFitness = null; + continue mainLoop; + } + } + } + + // "State is fully traversed."); + trajectoriesToExplore.remove(currentTrajectoryWithFitness); + currentTrajectoryWithFitness = null; + + } + // "Interrupted."); + + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java new file mode 100644 index 00000000..42985013 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java @@ -0,0 +1,108 @@ +package tools.refinery.store.dse.strategy; + +import tools.refinery.store.dse.DesignSpaceExplorationAdapter; +import tools.refinery.store.dse.Strategy; +import tools.refinery.store.dse.internal.Activation; +import tools.refinery.store.dse.objectives.Fitness; + +import java.util.Collection; + +public class DepthFirstStrategy implements Strategy { + + private DesignSpaceExplorationAdapter dseAdapter; + + private int maxDepth; + private boolean backTrackIfSolution = true; + + public DepthFirstStrategy() { + this(-1); + } + + public DepthFirstStrategy(int maxDepth) { + if (maxDepth < 0) { + this.maxDepth = Integer.MAX_VALUE; + } else { + this.maxDepth = maxDepth; + } + } + + public DepthFirstStrategy continueIfHardObjectivesFulfilled() { + backTrackIfSolution = false; + return this; + } + + @Override + public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { + this.dseAdapter = designSpaceExplorationAdapter; + } + + @Override + public void explore() { + mainloop: while (true) { + var globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + var isSuccessfulUndo = dseAdapter.backtrack(); + if (!isSuccessfulUndo) { +// "Global constraint is not satisfied and cannot backtrack." + break; + } + else { +// "Global constraint is not satisfied, backtrack." + continue; + } + } + + Fitness fitness = dseAdapter.calculateFitness(); + if (fitness.isSatisfiesHardObjectives()) { + dseAdapter.newSolution(); + if (backTrackIfSolution) { + var isSuccessfulUndo = dseAdapter.backtrack(); + if (!isSuccessfulUndo) { +// "Found a solution but cannot backtrack." + break; + } else { +// "Found a solution, backtrack." + continue; + } + } + } + + var depth = dseAdapter.getDepth(); + if (dseAdapter.getDepth() >= maxDepth) { + var isSuccessfulUndo = dseAdapter.backtrack(); + if (!isSuccessfulUndo) { +// "Reached max depth but cannot backtrack." + break; + } + } + + Collection activations; + do { + activations = dseAdapter.getUntraversedActivations(); + if (activations.isEmpty()) { + if (!dseAdapter.backtrack()) { + // "No more transitions from current state and cannot backtrack." + break mainloop; + } + else { + // "No more transitions from current state, backtrack." + continue; + } + } + } while (activations.isEmpty()); + + dseAdapter.fireRandomActivation(); +// if (dseAdapter.isCurrentInTrajectory()) { +// if (!dseAdapter.backtrack()) { +//// TODO: throw exception +//// "The new state is present in the trajectory but cannot backtrack. Should never happen!" +// break; +// } +// else { +//// "The new state is already visited in the trajectory, backtrack." +// continue; +// } +// } + } + } +} diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java new file mode 100644 index 00000000..e7cc60d6 --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -0,0 +1,274 @@ +package tools.refinery.store.dse; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.strategy.BestFirstStrategy; +import tools.refinery.store.dse.strategy.DepthFirstStrategy; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.query.view.AnySymbolView; +import tools.refinery.store.query.view.KeyOnlyView; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.internal.FileFormat; + +import java.util.List; + +import static tools.refinery.store.query.literal.Literals.not; + +public class CRAExamplesTest { + private static final Symbol name = Symbol.of("Name", 1, String.class); + +// private static final Symbol classModel = Symbol.of("ClassModel", 1); + private static final Symbol classElement = Symbol.of("ClassElement", 1); +// private static final Symbol feature = Symbol.of("Feature", 1); + private static final Symbol attribute = Symbol.of("Attribute", 1); + private static final Symbol method = Symbol.of("Method", 1); + +// private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); + private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); + private static final Symbol dataDependency = Symbol.of("DataDependency", 2); + private static final Symbol functionalDependency = Symbol.of("FunctionalDependency", 2); + + private static final Symbol features = Symbol.of("Features", 2); + private static final Symbol classes = Symbol.of("Classes", 2); + +// private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); + private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); +// private static final AnySymbolView featureView = new KeyOnlyView<>(feature); + private static final AnySymbolView attributeView = new KeyOnlyView<>(attribute); + private static final AnySymbolView methodView = new KeyOnlyView<>(method); +// private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); + private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); + private static final AnySymbolView dataDependencyView = new KeyOnlyView<>(dataDependency); + private static final AnySymbolView functionalDependencyView = new KeyOnlyView<>(functionalDependency); + private static final AnySymbolView featuresView = new KeyOnlyView<>(features); + private static final AnySymbolView classesView = new KeyOnlyView<>(classes); + + /*Example Transformation rules*/ + private static final RelationalQuery feature = Query.of("Feature", + (builder, f) -> builder.clause( + attributeView.call(f)) + .clause( + methodView.call(f)) + ); + + private static final RelationalQuery assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", + (builder, c, f) -> builder.clause( + classElementView.call(c), +// classesView.call(model, c), + encapsulatesView.call(c, f) + )); + + private static final RelationalQuery assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", + (builder, f, c1) -> builder.clause((c2) -> List.of( +// classModelView.call(model), + feature.call(f), + classElementView.call(c1), +// featuresView.call(model, f), + not(assignFeaturePreconditionHelper.call(c2, f)), + not(encapsulatesView.call(c1, f)) + ))); + + private static final RelationalQuery deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", + (builder, c) -> builder.clause((f) -> List.of( +// classModelView.call(model), + classElementView.call(c), +// featuresView.call(model, f), + not(encapsulatesView.call(c, f)) + ))); + + private static final RelationalQuery createClassPreconditionHelper = Query.of("CreateClassPreconditionHelper", + (builder, f, c) -> builder.clause( + classElementView.call(c), +// classesView.call(model, c), + encapsulatesView.call(c, f) + )); + + private static final RelationalQuery createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, f) -> builder.clause((c) -> List.of( +// classModelView.call(model), + feature.call(f), + not(createClassPreconditionHelper.call(f, c)) + ))); + + private static final RelationalQuery moveFeaturePrecondition = Query.of("MoveFeature", + (builder, c1, c2, f) -> builder.clause( +// classModelView.call(model), + classElementView.call(c1), + classElementView.call(c2), + c1.notEquivalent(c2), + feature.call(f), +// classesView.call(model, c1), +// classesView.call(model, c2), +// featuresView.call(model, f), + encapsulatesView.call(c1, f) + )); + + private static final TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", + assignFeaturePrecondition, + (model) -> { +// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); + var encapsulatesInterpretation = model.getInterpretation(encapsulates); + return ((Tuple activation) -> { + var feature = activation.get(0); + var classElement = activation.get(1); + +// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); + encapsulatesInterpretation.put(Tuple.of(classElement, feature), true); + }); + }); + + private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", + deleteEmptyClassPrecondition, + (model) -> { +// var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + // TODO: can we move dseAdapter outside? + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); + var classElement = activation.get(0); + +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + dseAdapter.deleteObject(Tuple.of(classElement)); + }); + }); + + private static final TransformationRule createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classElementInterpretation = model.getInterpretation(classElement); +// var classesInterpretation = model.getInterpretation(classes); + var encapsulatesInterpretation = model.getInterpretation(encapsulates); + return ((Tuple activation) -> { + // TODO: can we move dseAdapter outside? + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); + var feature = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + classElementInterpretation.put(newClassElement, true); +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true); + }); + }); + + private static final TransformationRule moveFeatureRule = new TransformationRule("MoveFeature", + moveFeaturePrecondition, + (model) -> { + var encapsulatesInterpretation = model.getInterpretation(encapsulates); + return ((Tuple activation) -> { + var classElement1 = activation.get(0); + var classElement2 = activation.get(1); + var feature = activation.get(2); + + encapsulatesInterpretation.put(Tuple.of(classElement1, feature), false); + encapsulatesInterpretation.put(Tuple.of(classElement2, feature), true); + }); + }); + + @Test + void craTest() { + var store = ModelStore.builder() + .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, + functionalDependency, name) + .with(ViatraModelQueryAdapter.builder() + .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, + deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, + moveFeaturePrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) +// .strategy(new DepthFirstStrategy(3).continueIfHardObjectivesFulfilled() + .strategy(new BestFirstStrategy(6).continueIfHardObjectivesFulfilled() +// .goOnOnlyIfFitnessIsBetter() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// dseAdapter.setRandom(1); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + +// var modelInterpretation = model.getInterpretation(classModel); + var nameInterpretation = model.getInterpretation(name); + var methodInterpretation = model.getInterpretation(method); + var attributeInterpretation = model.getInterpretation(attribute); + var dataDependencyInterpretation = model.getInterpretation(dataDependency); + var functionalDependencyInterpretation = model.getInterpretation(functionalDependency); + +// var modelElement = dseAdapter.createObject(); + var method1 = dseAdapter.createObject(); + var method1Id = method1.get(0); + var method2 = dseAdapter.createObject(); + var method2Id = method2.get(0); + var method3 = dseAdapter.createObject(); + var method3Id = method3.get(0); + var method4 = dseAdapter.createObject(); + var method4Id = method4.get(0); + var attribute1 = dseAdapter.createObject(); + var attribute1Id = attribute1.get(0); + var attribute2 = dseAdapter.createObject(); + var attribute2Id = attribute2.get(0); + var attribute3 = dseAdapter.createObject(); + var attribute3Id = attribute3.get(0); + var attribute4 = dseAdapter.createObject(); + var attribute4Id = attribute4.get(0); + var attribute5 = dseAdapter.createObject(); + var attribute5Id = attribute5.get(0); + + nameInterpretation.put(method1, "M1"); + nameInterpretation.put(method2, "M2"); + nameInterpretation.put(method3, "M3"); + nameInterpretation.put(method4, "M4"); + nameInterpretation.put(attribute1, "A1"); + nameInterpretation.put(attribute2, "A2"); + nameInterpretation.put(attribute3, "A3"); + nameInterpretation.put(attribute4, "A4"); + nameInterpretation.put(attribute5, "A5"); + + + +// modelInterpretation.put(modelElement, true); + methodInterpretation.put(method1, true); + methodInterpretation.put(method2, true); + methodInterpretation.put(method3, true); + methodInterpretation.put(method4, true); + attributeInterpretation.put(attribute1, true); + attributeInterpretation.put(attribute2, true); + attributeInterpretation.put(attribute3, true); + attributeInterpretation.put(attribute4, true); + attributeInterpretation.put(attribute5, true); + + dataDependencyInterpretation.put(Tuple.of(method1Id, attribute1Id), true); + dataDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); + dataDependencyInterpretation.put(Tuple.of(method2Id, attribute2Id), true); + dataDependencyInterpretation.put(Tuple.of(method3Id, attribute3Id), true); + dataDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); + dataDependencyInterpretation.put(Tuple.of(method4Id, attribute3Id), true); + dataDependencyInterpretation.put(Tuple.of(method4Id, attribute5Id), true); + + functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); + functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute4Id), true); + functionalDependencyInterpretation.put(Tuple.of(method2Id, attribute1Id), true); + functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute1Id), true); + functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); + functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true); + + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + System.out.println("states size: " + states.size()); + System.out.println("states: " + states); + var visualizer = model.getAdapter(ModelVisualizerAdapter.class); + visualizer.renderDesignSpace("test_output", FileFormat.SVG); + } + +} diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java new file mode 100644 index 00000000..911c0661 --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -0,0 +1,117 @@ +package tools.refinery.store.dse; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.strategy.BestFirstStrategy; +import tools.refinery.store.dse.strategy.DepthFirstStrategy; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.query.view.AnySymbolView; +import tools.refinery.store.query.view.KeyOnlyView; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.ModelVisualizerAdapter; +import tools.refinery.visualization.internal.FileFormat; + +public class DebugTest { + private static final Symbol classModel = Symbol.of("ClassModel", 1); + private static final Symbol classElement = Symbol.of("ClassElement", 1); + private static final Symbol feature = Symbol.of("Feature", 1); + + private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); + private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); + + private static final Symbol features = Symbol.of("Features", 2); + private static final Symbol classes = Symbol.of("Classes", 2); + + private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); + private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); + private static final AnySymbolView featureView = new KeyOnlyView<>(feature); + private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); + private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); + private static final AnySymbolView featuresView = new KeyOnlyView<>(features); + private static final AnySymbolView classesView = new KeyOnlyView<>(classes); + + + @Test + void BFSTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createFeatureRule = new TransformationRule("CreateFeature", + createFeaturePrecondition, + (model) -> { + var featuresInterpretation = model.getInterpretation(features); + var featureInterpretation = model.getInterpretation(feature); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + featureInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition, createFeaturePrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule, createFeatureRule) + .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() +// .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() +// .goOnOnlyIfFitnessIsBetter() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// dseAdapter.setRandom(1); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + var classElementInterpretation = model.getInterpretation(classElement); + var modelElement = dseAdapter.createObject(); + modelElementInterpretation.put(modelElement, true); + classElementInterpretation.put(modelElement, true); + queryEngine.flushChanges(); + + + var states = dseAdapter.explore(); + var visualizer = model.getAdapter(ModelVisualizerAdapter.class); + visualizer.renderDesignSpace("test_output", FileFormat.SVG); + System.out.println("states size: " + states.size()); + System.out.println("states: " + states); + + } +} diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java new file mode 100644 index 00000000..c454f4ec --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java @@ -0,0 +1,487 @@ +package tools.refinery.store.dse; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.strategy.BestFirstStrategy; +import tools.refinery.store.dse.strategy.DepthFirstStrategy; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.query.view.AnySymbolView; +import tools.refinery.store.query.view.KeyOnlyView; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.visualization.ModelVisualizerAdapter; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class DesignSpaceExplorationTest { +// private static final Symbol namedElement = Symbol.of("NamedElement", 1); +// private static final Symbol attribute = Symbol.of("Attribute", 1); +// private static final Symbol method = Symbol.of("Method", 1); +// private static final Symbol dataDependency = Symbol.of("DataDependency", 2); +// private static final Symbol functionalDependency = Symbol.of("FunctionalDependency", 2); + + private static final Symbol classModel = Symbol.of("ClassModel", 1); + private static final Symbol classElement = Symbol.of("ClassElement", 1); + private static final Symbol feature = Symbol.of("Feature", 1); + + private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); + private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); + + private static final Symbol features = Symbol.of("Features", 2); + private static final Symbol classes = Symbol.of("Classes", 2); + + private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); + private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); + private static final AnySymbolView featureView = new KeyOnlyView<>(feature); + private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); + private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); + private static final AnySymbolView featuresView = new KeyOnlyView<>(features); + private static final AnySymbolView classesView = new KeyOnlyView<>(classes); + + @Test + void createObjectTest() { + var store = ModelStore.builder() + .with(ViatraModelQueryAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + + assertEquals(0, dseAdapter.getModelSize()); + + var newModel = dseAdapter.createObject(); + var newModelId = newModel.get(0); + var newClass1 = dseAdapter.createObject(); + var newClass1Id = newClass1.get(0); + var newClass2 = dseAdapter.createObject(); + var newClass2Id = newClass2.get(0); + var newField = dseAdapter.createObject(); + var newFieldId = newField.get(0); + + assertEquals(0, newModelId); + assertEquals(1, newClass1Id); + assertEquals(2, newClass2Id); + assertEquals(3, newFieldId); + assertEquals(4, dseAdapter.getModelSize()); + } + + @Test + void deleteMiddleObjectTest() { + var store = ModelStore.builder() + .with(ViatraModelQueryAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + + assertEquals(0, dseAdapter.getModelSize()); + + var newObject0 = dseAdapter.createObject(); + var newObject0Id = newObject0.get(0); + var newObject1 = dseAdapter.createObject(); + var newObject1Id = newObject1.get(0); + var newObject2 = dseAdapter.createObject(); + var newObject2Id = newObject2.get(0); + var newObject3 = dseAdapter.createObject(); + var newObject3Id = newObject3.get(0); + + assertEquals(0, newObject0Id); + assertEquals(1, newObject1Id); + assertEquals(2, newObject2Id); + assertEquals(3, newObject3Id); + assertEquals(4, dseAdapter.getModelSize()); + + dseAdapter.deleteObject(newObject1); + assertEquals(4, dseAdapter.getModelSize()); + + var newObject4 = dseAdapter.createObject(); + var newObject4Id = newObject4.get(0); + assertEquals(4, newObject4Id); + assertEquals(5, dseAdapter.getModelSize()); + + dseAdapter.deleteObject(newObject4); + assertEquals(5, dseAdapter.getModelSize()); + } + + @Test + void DFSTrivialTest() { + var store = ModelStore.builder() + .symbols(classModel) + .with(ViatraModelQueryAdapter.builder()) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .strategy(new DepthFirstStrategy(0))) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + + var states = dseAdapter.explore(); + assertEquals(1, states.size()); + assertEquals(0, states.iterator().next()); + } + + @Test + void DFSOneRuleTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule) + .strategy(new DepthFirstStrategy(4) + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + assertEquals(1, states.size()); + assertEquals(0, states.iterator().next()); + } + + @Test + void DFSContinueTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule) + .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + var iterator = states.iterator(); + assertEquals(5, states.size()); + assertEquals(0, iterator.next()); + assertEquals(1, iterator.next()); + assertEquals(2, iterator.next()); + assertEquals(3, iterator.next()); + assertEquals(4, iterator.next()); + } + + @Test + void DFSCompletenessTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createFeatureRule = new TransformationRule("CreateFeature", + createFeaturePrecondition, + (model) -> { + var featuresInterpretation = model.getInterpretation(features); + var featureInterpretation = model.getInterpretation(feature); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + featureInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition, createFeaturePrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule, createFeatureRule) + .strategy(new DepthFirstStrategy(10).continueIfHardObjectivesFulfilled() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + assertEquals(2047, states.size()); + } + + @Test + void BFSTrivialTest() { + var store = ModelStore.builder() + .symbols(classModel) + .with(ViatraModelQueryAdapter.builder()) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .strategy(new BestFirstStrategy(0))) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + + var states = dseAdapter.explore(); + assertEquals(1, states.size()); + assertEquals(0, states.iterator().next()); + } + + @Test + void BFSOneRuleTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule) + .strategy(new BestFirstStrategy(4) + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + assertEquals(1, states.size()); + assertEquals(0, states.iterator().next()); + } + + @Test + void BFSContinueTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule) + .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + var iterator = states.iterator(); + assertEquals(5, states.size()); + assertEquals(0, iterator.next()); + assertEquals(1, iterator.next()); + assertEquals(2, iterator.next()); + assertEquals(3, iterator.next()); + assertEquals(4, iterator.next()); + } + + @Test + void BFSCompletenessTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createFeatureRule = new TransformationRule("CreateFeature", + createFeaturePrecondition, + (model) -> { + var featuresInterpretation = model.getInterpretation(features); + var featureInterpretation = model.getInterpretation(feature); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + featureInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition, createFeaturePrecondition)) + .with(ModelVisualizerAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule, createFeatureRule) + .strategy(new BestFirstStrategy(10).continueIfHardObjectivesFulfilled() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + assertEquals(2047, states.size()); + } + +} diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java new file mode 100644 index 00000000..a32d392b --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java @@ -0,0 +1,403 @@ +package tools.refinery.store.dse; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.dnf.Query; +import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.query.view.AnySymbolView; +import tools.refinery.store.query.view.KeyOnlyView; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static tools.refinery.store.query.literal.Literals.not; +import static tools.refinery.store.dse.tests.QueryAssertions.assertResults; + +public class TransformationRuleTest { + + private static final Symbol classModel = Symbol.of("ClassModel", 1); + private static final Symbol classElement = Symbol.of("ClassElement", 1); + private static final Symbol feature = Symbol.of("Feature", 1); + + private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); + private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); + + private static final Symbol features = Symbol.of("Features", 2); + private static final Symbol classes = Symbol.of("Classes", 2); + + private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); + private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); + private static final AnySymbolView featureView = new KeyOnlyView<>(feature); + private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); + private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); + private static final AnySymbolView featuresView = new KeyOnlyView<>(features); + private static final AnySymbolView classesView = new KeyOnlyView<>(classes); + + @Test + void activationsTest() { + var assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", + (builder, model, c, f) -> builder.clause( + classElementView.call(c), + classesView.call(model, c), + encapsulatesView.call(c, f) + )); + + var assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f) + -> builder.clause((model, c1) -> List.of( + classModelView.call(model), + featureView.call(f), + classElementView.call(c2), + featuresView.call(model, f), + classesView.call(model, c1), + not(assignFeaturePreconditionHelper.call(model, c2, f)), + not(encapsulatesView.call(c2, f)) + ))); + + var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", + (builder, model, c) -> builder.clause((f) -> List.of( + classModelView.call(model), + classElementView.call(c), + featuresView.call(model, f), + not(encapsulatesView.call(c, f)) + ))); + + TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", + assignFeaturePrecondition, + (model) -> { + var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); + return ((Tuple activation) -> { + var feature = activation.get(0); + var classElement = activation.get(1); + + isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); + }); + }); + + TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + }); + }); + + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(assignFeaturePrecondition, assignFeaturePreconditionHelper, + deleteEmptyClassPrecondition)) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + assignFeatureRule.prepare(model, queryEngine); + deleteEmptyClassRule.prepare(model, queryEngine); + + var classModelInterpretation = model.getInterpretation(classModel); + var classElementInterpretation = model.getInterpretation(classElement); + var featureInterpretation = model.getInterpretation(feature); + var featuresInterpretation = model.getInterpretation(features); + var classesInterpretation = model.getInterpretation(classes); + + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var newModel = dseAdapter.createObject(); + var newModelId = newModel.get(0); + var newClass1 = dseAdapter.createObject(); + var newClass1Id = newClass1.get(0); + var newClass2 = dseAdapter.createObject(); + var newClass2Id = newClass2.get(0); + var newField = dseAdapter.createObject(); + var newFieldId = newField.get(0); + + classModelInterpretation.put(newModel, true); + classElementInterpretation.put(newClass1, true); + classElementInterpretation.put(newClass2, true); + featureInterpretation.put(newField, true); + classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); + classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); + featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); + + queryEngine.flushChanges(); + + var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsSets(); + var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsSets(); + + assertResults(Map.of( + Tuple.of(newClass1Id, newFieldId), true, + Tuple.of(newClass2Id, newFieldId), true + ), assignFeatureRuleActivations); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRuleActivations); + } + + @Test + void randomActivationTest() { + var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", + (builder, model, c) -> builder.clause((f) -> List.of( + classModelView.call(model), + classElementView.call(c), + featuresView.call(model, f), + not(encapsulatesView.call(c, f)) + ))); + + TransformationRule deleteEmptyClassRule0 = new TransformationRule("DeleteEmptyClass0", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + }); + }, + 0L); + + TransformationRule deleteEmptyClassRule1 = new TransformationRule("DeleteEmptyClass1", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + }); + }, + 78634L); + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(deleteEmptyClassPrecondition)) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + deleteEmptyClassRule0.prepare(model, queryEngine); + deleteEmptyClassRule1.prepare(model, queryEngine); + + var classModelInterpretation = model.getInterpretation(classModel); + var classElementInterpretation = model.getInterpretation(classElement); + var featureInterpretation = model.getInterpretation(feature); + var featuresInterpretation = model.getInterpretation(features); + var classesInterpretation = model.getInterpretation(classes); + + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var newModel = dseAdapter.createObject(); + var newModelId = newModel.get(0); + var newClass1 = dseAdapter.createObject(); + var newClass1Id = newClass1.get(0); + var newClass2 = dseAdapter.createObject(); + var newClass2Id = newClass2.get(0); + var newField = dseAdapter.createObject(); + var newFieldId = newField.get(0); + + classModelInterpretation.put(newModel, true); + classElementInterpretation.put(newClass1, true); + classElementInterpretation.put(newClass2, true); + featureInterpretation.put(newField, true); + classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); + classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); + featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); + + queryEngine.flushChanges(); + + + var activation0 = deleteEmptyClassRule0.getRandomActivation().activation(); + var activation1 = deleteEmptyClassRule1.getRandomActivation().activation(); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRule0.getAllActivationsAsSets()); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRule1.getAllActivationsAsSets()); + + assertEquals(Tuple.of(newModelId, newClass2Id), activation0); + assertEquals(Tuple.of(newModelId, newClass1Id), activation1); + + } + + @Test + void fireTest() { + var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", + (builder, model, c) -> builder.clause((f) -> List.of( + classModelView.call(model), + classElementView.call(c), + featuresView.call(model, f), + not(encapsulatesView.call(c, f)) + ))); + + TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(deleteEmptyClassPrecondition)) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + deleteEmptyClassRule.prepare(model, queryEngine); + + var classModelInterpretation = model.getInterpretation(classModel); + var classElementInterpretation = model.getInterpretation(classElement); + var featureInterpretation = model.getInterpretation(feature); + var featuresInterpretation = model.getInterpretation(features); + var classesInterpretation = model.getInterpretation(classes); + + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var newModel = dseAdapter.createObject(); + var newModelId = newModel.get(0); + var newClass1 = dseAdapter.createObject(); + var newClass1Id = newClass1.get(0); + var newClass2 = dseAdapter.createObject(); + var newClass2Id = newClass2.get(0); + var newField = dseAdapter.createObject(); + var newFieldId = newField.get(0); + + classModelInterpretation.put(newModel, true); + classElementInterpretation.put(newClass1, true); + classElementInterpretation.put(newClass2, true); + featureInterpretation.put(newField, true); + classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); + classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); + featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); + + queryEngine.flushChanges(); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRule.getAllActivationsAsSets()); + + + deleteEmptyClassRule.fireActivation(Tuple.of(0, 1)); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), false, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRule.getAllActivationsAsSets()); + } + + @Test + void randomFireTest() { + var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", + (builder, model, c) -> builder.clause((f) -> List.of( + classModelView.call(model), + classElementView.call(c), + featuresView.call(model, f), + not(encapsulatesView.call(c, f)) + ))); + + TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass0", + deleteEmptyClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var modelElement = activation.get(0); + var classElement = activation.get(1); + + classesInterpretation.put(Tuple.of(modelElement, classElement), false); + classElementInterpretation.put(Tuple.of(classElement), false); + }); + }, + 0L); + + var store = ModelStore.builder() + .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) + .with(ViatraModelQueryAdapter.builder() + .queries(deleteEmptyClassPrecondition)) + .with(DesignSpaceExplorationAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + deleteEmptyClassRule.prepare(model, queryEngine); + + var classModelInterpretation = model.getInterpretation(classModel); + var classElementInterpretation = model.getInterpretation(classElement); + var featureInterpretation = model.getInterpretation(feature); + var featuresInterpretation = model.getInterpretation(features); + var classesInterpretation = model.getInterpretation(classes); + + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var newModel = dseAdapter.createObject(); + var newModelId = newModel.get(0); + var newClass1 = dseAdapter.createObject(); + var newClass1Id = newClass1.get(0); + var newClass2 = dseAdapter.createObject(); + var newClass2Id = newClass2.get(0); + var newField = dseAdapter.createObject(); + var newFieldId = newField.get(0); + + classModelInterpretation.put(newModel, true); + classElementInterpretation.put(newClass1, true); + classElementInterpretation.put(newClass2, true); + featureInterpretation.put(newField, true); + classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); + classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); + featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); + + queryEngine.flushChanges(); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), true + ), deleteEmptyClassRule.getAllActivationsAsSets()); + + deleteEmptyClassRule.fireRandomActivation(); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), true, + Tuple.of(newModelId, newClass2Id), false + ), deleteEmptyClassRule.getAllActivationsAsSets()); + + deleteEmptyClassRule.fireRandomActivation(); + + assertResults(Map.of( + Tuple.of(newModelId, newClass1Id), false, + Tuple.of(newModelId, newClass2Id), false + ), deleteEmptyClassRule.getAllActivationsAsSets()); + + } +} diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java new file mode 100644 index 00000000..be514eaf --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/tests/QueryAssertions.java @@ -0,0 +1,57 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.tests; + +import org.junit.jupiter.api.function.Executable; +import tools.refinery.store.query.resultset.ResultSet; +import tools.refinery.store.tuple.Tuple; + +import java.util.*; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.jupiter.api.Assertions.assertAll; + +public final class QueryAssertions { + private QueryAssertions() { + throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); + } + + public static void assertNullableResults(Map> expected, ResultSet resultSet) { + var nullableValuesMap = new LinkedHashMap(expected.size()); + for (var entry : expected.entrySet()) { + nullableValuesMap.put(entry.getKey(), entry.getValue().orElse(null)); + } + assertResults(nullableValuesMap, resultSet); + } + + public static void assertResults(Map expected, ResultSet resultSet) { + var defaultValue = resultSet.getQuery().defaultValue(); + var filteredExpected = new LinkedHashMap(); + var executables = new ArrayList(); + for (var entry : expected.entrySet()) { + var key = entry.getKey(); + var value = entry.getValue(); + if (!Objects.equals(value, defaultValue)) { + filteredExpected.put(key, value); + } + executables.add(() -> assertThat("value for key " + key,resultSet.get(key), is(value))); + } + executables.add(() -> assertThat("results size", resultSet.size(), is(filteredExpected.size()))); + + var actual = new LinkedHashMap(); + var cursor = resultSet.getAll(); + while (cursor.move()) { + var key = cursor.getKey(); + var previous = actual.put(key, cursor.getValue()); + assertThat("duplicate value for key " + key, previous, nullValue()); + } + executables.add(() -> assertThat("results cursor", actual, is(filteredExpected))); + + assertAll(executables); + } +} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/ActionFactory.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/ActionFactory.java deleted file mode 100644 index e2f452c3..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/ActionFactory.java +++ /dev/null @@ -1,9 +0,0 @@ -package tools.refinery.store.query.dse; - -import org.eclipse.collections.api.block.procedure.Procedure; -import tools.refinery.store.model.Model; -import tools.refinery.store.tuple.Tuple; - -public interface ActionFactory { - Procedure prepare(Model model); -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java deleted file mode 100644 index ca780855..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationAdapter.java +++ /dev/null @@ -1,61 +0,0 @@ -package tools.refinery.store.query.dse; - -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.map.Version; -import tools.refinery.store.query.dse.internal.Activation; -import tools.refinery.store.query.dse.internal.DesignSpaceExplorationBuilderImpl; -import tools.refinery.store.query.dse.objectives.Fitness; -import tools.refinery.store.query.dse.objectives.ObjectiveComparatorHelper; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.Tuple1; - -import java.util.Collection; -import java.util.List; -import java.util.Random; - -public interface DesignSpaceExplorationAdapter extends ModelAdapter { - @Override - DesignSpaceExplorationStoreAdapter getStoreAdapter(); - - static DesignSpaceExplorationBuilder builder() { - return new DesignSpaceExplorationBuilderImpl(); - } - - Collection explore(); - - public int getModelSize(); - - public Tuple1 createObject(); - - public Tuple deleteObject(Tuple tuple); - - public boolean checkGlobalConstraints(); - - public boolean backtrack(); - - public Fitness calculateFitness(); - - public void newSolution(); - - public int getDepth(); - - public Collection getUntraversedActivations(); - - public boolean fireActivation(Activation activation); - - public void fireRandomActivation(); - - public boolean isCurrentInTrajectory(); - - public List getTrajectory(); - - public boolean isCurrentStateAlreadyTraversed(); - - public ObjectiveComparatorHelper getObjectiveComparatorHelper(); - - public void restoreTrajectory(List trajectory); - - public void setRandom(Random random); - - public void setRandom(long seed); -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java deleted file mode 100644 index 58eac681..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationBuilder.java +++ /dev/null @@ -1,44 +0,0 @@ -package tools.refinery.store.query.dse; - -import tools.refinery.store.adapter.ModelAdapterBuilder; -import tools.refinery.store.query.dnf.AnyQuery; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.query.dse.internal.TransformationRule; -import tools.refinery.store.query.dse.objectives.Objective; - -import java.util.Collection; -import java.util.List; - -public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { - default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRules) { - return transformations(List.of(transformationRules)); - } - - default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { - transformationRules.forEach(this::transformation); - return this; - } - - default DesignSpaceExplorationBuilder globalConstraints(RelationalQuery... globalConstraints) { - return globalConstraints(List.of(globalConstraints)); - } - - default DesignSpaceExplorationBuilder globalConstraints(Collection globalConstraints) { - globalConstraints.forEach(this::globalConstraint); - return this; - } - - default DesignSpaceExplorationBuilder objectives(Objective... objectives) { - return objectives(List.of(objectives)); - } - - default DesignSpaceExplorationBuilder objectives(Collection objectives) { - objectives.forEach(this::objective); - return this; - } - - DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule); - DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint); - DesignSpaceExplorationBuilder objective(Objective objective); - DesignSpaceExplorationBuilder strategy(Strategy strategy); -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationStoreAdapter.java deleted file mode 100644 index 21b10f0e..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/DesignSpaceExplorationStoreAdapter.java +++ /dev/null @@ -1,6 +0,0 @@ -package tools.refinery.store.query.dse; - -import tools.refinery.store.adapter.ModelStoreAdapter; - -public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter { -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/Strategy.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/Strategy.java deleted file mode 100644 index 0aeea818..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/Strategy.java +++ /dev/null @@ -1,10 +0,0 @@ -package tools.refinery.store.query.dse; - -import tools.refinery.store.model.Model; - -public interface Strategy { - - public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter); - - public void explore(); -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/Activation.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/Activation.java deleted file mode 100644 index 82a4c978..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/Activation.java +++ /dev/null @@ -1,9 +0,0 @@ -package tools.refinery.store.query.dse.internal; - -import tools.refinery.store.tuple.Tuple; - -public record Activation(TransformationRule transformationRule, Tuple activation) { - public boolean fire() { - return transformationRule.fireActivation(activation); - } -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java deleted file mode 100644 index d2a8e6fa..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ /dev/null @@ -1,283 +0,0 @@ -package tools.refinery.store.query.dse.internal; - -import tools.refinery.store.map.Version; -import tools.refinery.store.model.Interpretation; -import tools.refinery.store.model.Model; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; -import tools.refinery.store.query.dse.DesignSpaceExplorationStoreAdapter; -import tools.refinery.store.query.dse.Strategy; -import tools.refinery.store.query.dse.objectives.Fitness; -import tools.refinery.store.query.dse.objectives.Objective; -import tools.refinery.store.query.dse.objectives.ObjectiveComparatorHelper; -import tools.refinery.store.query.resultset.ResultSet; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.Tuple1; -import tools.refinery.visualization.ModelVisualizerAdapter; - -import java.util.*; - -public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExplorationAdapter { - static final Symbol NODE_COUNT_SYMBOL = Symbol.of("MODEL_SIZE", 0, Integer.class, 0); - private final Model model; - private final ModelQueryAdapter queryEngine; - private final DesignSpaceExplorationStoreAdapterImpl storeAdapter; - private final LinkedHashSet transformationRules; - private final LinkedHashSet globalConstraints; - private final List objectives; - private final LinkedHashSet> globalConstraintResultSets = new LinkedHashSet<>(); - private final Interpretation sizeInterpretation; - private final Strategy strategy; - - private ObjectiveComparatorHelper objectiveComparatorHelper; - private List trajectory = new LinkedList<>(); - private Fitness lastFitness; - private final LinkedHashSet solutions = new LinkedHashSet<>(); - private Map> statesAndUntraversedActivations; - private Map> statesAndTraversedActivations; - private Random random = new Random(); - private boolean isNewState = false; - private final boolean isVisualizationEnabled; - private final ModelVisualizerAdapter modelVisualizerAdapter; - - public List getTrajectory() { - return new LinkedList<>(trajectory); - } - - public DesignSpaceExplorationAdapterImpl(Model model, DesignSpaceExplorationStoreAdapterImpl storeAdapter) { - this.model = model; - this.storeAdapter = storeAdapter; - this.sizeInterpretation = model.getInterpretation(NODE_COUNT_SYMBOL); - queryEngine = model.getAdapter(ModelQueryAdapter.class); - - globalConstraints = storeAdapter.getGlobalConstraints(); - for (var constraint : globalConstraints) { - globalConstraintResultSets.add(queryEngine.getResultSet(constraint)); - } - - transformationRules = storeAdapter.getTransformationSpecifications(); - for (var rule : transformationRules) { - rule.prepare(model, queryEngine); - } - - objectives = storeAdapter.getObjectives(); - statesAndUntraversedActivations = new HashMap<>(); - statesAndTraversedActivations = new HashMap<>(); - strategy = storeAdapter.getStrategy(); - modelVisualizerAdapter = model.tryGetAdapter(ModelVisualizerAdapter.class).orElse(null); - isVisualizationEnabled = modelVisualizerAdapter != null; - - } - - @Override - public Model getModel() { - return model; - } - - @Override - public DesignSpaceExplorationStoreAdapter getStoreAdapter() { - return storeAdapter; - } - - @Override - public LinkedHashSet explore() { - var state = model.commit(); - trajectory.add(state); - statesAndUntraversedActivations.put(state, getAllActivations()); - statesAndTraversedActivations.put(state, new LinkedHashSet<>()); - strategy.initStrategy(this); - strategy.explore(); - return solutions; - } - - @Override - public int getModelSize() { - return sizeInterpretation.get(Tuple.of()); - } - - @Override - public Tuple1 createObject() { - var newNodeId = getModelSize(); - sizeInterpretation.put(Tuple.of(), newNodeId + 1); - return Tuple.of(newNodeId); - } - - @Override - public Tuple deleteObject(Tuple tuple) { - if (tuple.getSize() != 1) { - throw new IllegalArgumentException("Tuple size must be 1"); - } -// TODO: implement more efficient deletion -// if (tuple.get(0) == getModelSize() - 1) { -// sizeInterpretation.put(Tuple.of(), getModelSize() - 1); -// } - return tuple; - } - - @Override - public boolean checkGlobalConstraints() { - for (var resultSet : globalConstraintResultSets) { - if (resultSet.size() > 0) { - return false; - } - } - return true; - } - - @Override - public boolean backtrack() { - if (trajectory.size() < 2) { - return false; - } - if (isVisualizationEnabled) { - modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 1), - trajectory.get(trajectory.size() - 2), "backtrack"); - } - model.restore(trajectory.get(trajectory.size() - 2)); - trajectory.remove(trajectory.size() - 1); - return true; - } - - @Override - public void restoreTrajectory(List trajectory) { - model.restore(trajectory.get(trajectory.size() - 1)); -// if (isVisualizationEnabled) { -// modelVisualizerAdapter.addTransition(this.trajectory.get(trajectory.size() - 1), -// trajectory.get(trajectory.size() - 1), "restore"); -// } - this.trajectory = trajectory; - - } - - @Override - public void setRandom(Random random) { - this.random = random; - } - - @Override - public void setRandom(long seed) { - this.random = new Random(seed); - } - - @Override - public Fitness calculateFitness() { - Fitness result = new Fitness(); - boolean satisfiesHardObjectives = true; - for (Objective objective : objectives) { - var fitness = objective.getFitness(this); - result.put(objective.getName(), fitness); - if (objective.isHardObjective() && !objective.satisfiesHardObjective(fitness)) { - satisfiesHardObjectives = false; - } - } - result.setSatisfiesHardObjectives(satisfiesHardObjectives); - - lastFitness = result; - - return result; - } - - @Override - public void newSolution() { - var state = model.getState(); - solutions.add(state); - if (isVisualizationEnabled) { - modelVisualizerAdapter.addSolution(state); - } - } - - @Override - public int getDepth() { - return trajectory.size() - 1; - } - - public LinkedHashSet getUntraversedActivations() { -// return statesAndUntraversedActivations.get(model.getState()); - LinkedHashSet untraversedActivations = new LinkedHashSet<>(); - for (Activation activation : getAllActivations()) { - if (!statesAndTraversedActivations.get(model.getState()).contains(activation)) { - untraversedActivations.add(activation); - } - } - - return untraversedActivations; - } - - @Override - public boolean fireActivation(Activation activation) { - if (activation == null) { - return false; - } - var previousState = model.getState(); - if (!statesAndUntraversedActivations.get(previousState).contains(activation)) { -// TODO: throw exception? - return false; - } - if (!activation.fire()) { - return false; - } - statesAndUntraversedActivations.get(previousState).remove(activation); - statesAndTraversedActivations.get(previousState).add(activation); - var newState = model.commit(); - trajectory.add(newState); - isNewState = !statesAndUntraversedActivations.containsKey(newState); - statesAndUntraversedActivations.put(newState, getAllActivations()); - statesAndTraversedActivations.put(newState, new LinkedHashSet<>()); - if (isVisualizationEnabled) { - if (isNewState) { - modelVisualizerAdapter.addState(newState); - } - modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 2), - trajectory.get(trajectory.size() - 1), activation.transformationRule().getName(), - activation.activation()); - } - return true; - } - - @Override - public void fireRandomActivation() { - var activations = getUntraversedActivations(); - if (activations.isEmpty()) { -// TODO: throw exception - return; - } - int index = random.nextInt(activations.size()); - var iterator = activations.iterator(); - while (index-- > 0) { - iterator.next(); - } - var activationId = iterator.next(); - fireActivation(activationId); - } - - @Override - public boolean isCurrentInTrajectory() { - return trajectory.contains(model.getState()); - } - - public LinkedHashSet getAllActivations() { - LinkedHashSet result = new LinkedHashSet<>(); - for (var rule : transformationRules) { - result.addAll(rule.getAllActivations()); - } - return result; - } - - public boolean isCurrentStateAlreadyTraversed() { -// TODO: check isomorphism? - return !isNewState; - } - - public Fitness getLastFitness() { - return lastFitness; - } - - public ObjectiveComparatorHelper getObjectiveComparatorHelper() { - if (objectiveComparatorHelper == null) { - objectiveComparatorHelper = new ObjectiveComparatorHelper(objectives); - } - return objectiveComparatorHelper; - } -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java deleted file mode 100644 index 638ec641..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationBuilderImpl.java +++ /dev/null @@ -1,62 +0,0 @@ -package tools.refinery.store.query.dse.internal; - -import tools.refinery.store.adapter.AbstractModelAdapterBuilder; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.query.dse.DesignSpaceExplorationBuilder; -import tools.refinery.store.query.dse.Strategy; -import tools.refinery.store.query.dse.objectives.Objective; - -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; - -public class DesignSpaceExplorationBuilderImpl - extends AbstractModelAdapterBuilder - implements DesignSpaceExplorationBuilder { - private final LinkedHashSet transformationSpecifications = new LinkedHashSet<>(); - private final LinkedHashSet globalConstraints = new LinkedHashSet<>(); - private final List objectives = new LinkedList<>(); - private Strategy strategy; - - @Override - protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) { - return new DesignSpaceExplorationStoreAdapterImpl(store, transformationSpecifications, globalConstraints, - objectives, strategy); - } - - @Override - public DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule) { - checkNotConfigured(); - transformationSpecifications.add(transformationRule); - return this; - } - - @Override - public DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint) { - checkNotConfigured(); - globalConstraints.add(globalConstraint); - return this; - } - - @Override - public DesignSpaceExplorationBuilder objective(Objective objective) { - checkNotConfigured(); - objectives.add(objective); - return this; - } - - @Override - public DesignSpaceExplorationBuilder strategy(Strategy strategy) { - checkNotConfigured(); - this.strategy = strategy; - return this; - } - - @Override - protected void doConfigure(ModelStoreBuilder storeBuilder) { - storeBuilder.symbols(DesignSpaceExplorationAdapterImpl.NODE_COUNT_SYMBOL); - super.doConfigure(storeBuilder); - } -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java deleted file mode 100644 index f58715b7..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java +++ /dev/null @@ -1,57 +0,0 @@ -package tools.refinery.store.query.dse.internal; - -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.query.dse.DesignSpaceExplorationStoreAdapter; -import tools.refinery.store.query.dse.Strategy; -import tools.refinery.store.query.dse.objectives.Objective; - -import java.util.LinkedHashSet; -import java.util.List; - -public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { - private final ModelStore store; - private final LinkedHashSet transformationSpecifications; - private final LinkedHashSet globalConstraints; - private final List objectives; - private final Strategy strategy; - - public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, - LinkedHashSet transformationSpecifications, - LinkedHashSet globalConstraints, - List objectives, Strategy strategy) { - this.store = store; - this.transformationSpecifications = transformationSpecifications; - this.globalConstraints = globalConstraints; - this.objectives = objectives; - this.strategy = strategy; - } - - @Override - public ModelStore getStore() { - return store; - } - - @Override - public ModelAdapter createModelAdapter(Model model) { - return new DesignSpaceExplorationAdapterImpl(model, this); - } - - public LinkedHashSet getTransformationSpecifications() { - return transformationSpecifications; - } - - public LinkedHashSet getGlobalConstraints() { - return globalConstraints; - } - - public List getObjectives() { - return objectives; - } - - public Strategy getStrategy() { - return strategy; - } -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java deleted file mode 100644 index 950ca930..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/internal/TransformationRule.java +++ /dev/null @@ -1,84 +0,0 @@ -package tools.refinery.store.query.dse.internal; - -import org.eclipse.collections.api.block.procedure.Procedure; -import tools.refinery.store.model.Model; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.query.dse.ActionFactory; -import tools.refinery.store.query.resultset.OrderedResultSet; -import tools.refinery.store.query.resultset.ResultSet; -import tools.refinery.store.tuple.Tuple; - -import java.util.LinkedHashSet; -import java.util.Random; - -public class TransformationRule { - - private final String name; - private final RelationalQuery precondition; - private final ActionFactory actionFactory; - private Procedure action; - private OrderedResultSet activations; - private Random random; - private ModelQueryAdapter queryEngine; - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) { - this(name, precondition, actionFactory, new Random()); - } - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) { - this(name, precondition, actionFactory, new Random(seed)); - } - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) { - this.name = name; - this.precondition = precondition; - this.actionFactory = actionFactory; - this.random = random; - } - public boolean prepare(Model model, ModelQueryAdapter queryEngine) { - action = actionFactory.prepare(model); - this.queryEngine = queryEngine; - activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); - return true; - } - - public boolean fireActivation(Tuple activation) { - action.accept(activation); - queryEngine.flushChanges(); - return true; - } - - public boolean fireRandomActivation() { - return getRandomActivation().fire(); - } - - public String getName() { - return name; - } - - public RelationalQuery getPrecondition() { - return precondition; - } - - public ResultSet getAllActivationsAsSets() { - return activations; - } - - public LinkedHashSet getAllActivations() { - var result = new LinkedHashSet(); - var cursor = activations.getAll(); - while (cursor.move()) { - result.add(new Activation(this, cursor.getKey())); - } - return result; - } - - public Activation getRandomActivation() { - return new Activation(this, activations.getKey(random.nextInt(activations.size()))); - } - - public Activation getActivation(int index) { - return new Activation(this, activations.getKey(index)); - } -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/AlwaysSatisfiedDummyHardObjective.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/AlwaysSatisfiedDummyHardObjective.java deleted file mode 100644 index 26744d94..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/AlwaysSatisfiedDummyHardObjective.java +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.query.dse.objectives; - -import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; - -/** - * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution. - * - * @author Andras Szabolcs Nagy - * - */ -public class AlwaysSatisfiedDummyHardObjective extends BaseObjective { - - private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective"; - - public AlwaysSatisfiedDummyHardObjective() { - super(DEFAULT_NAME); - } - - public AlwaysSatisfiedDummyHardObjective(String name) { - super(name); - } - - @Override - public Double getFitness(DesignSpaceExplorationAdapter context) { - return 0d; - } - - @Override - public boolean isHardObjective() { - return true; - } - - @Override - public boolean satisfiesHardObjective(Double fitness) { - return true; - } - - @Override - public Objective createNew() { - return this; - } - -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java deleted file mode 100644 index db8d5d7e..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/BaseObjective.java +++ /dev/null @@ -1,131 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.query.dse.objectives; - -import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; - -import java.util.Comparator; -import java.util.Objects; - -/** - * This abstract class implements the basic functionality of an objective ({@link Objective} namely its name, - * comparator, level and fitness hard constraint. - * - * @author Andras Szabolcs Nagy - * - */ -public abstract class BaseObjective implements Objective { - - protected final String name; - protected Comparator comparator = Comparators.HIGHER_IS_BETTER; - - protected double fitnessConstraint; - protected boolean isThereFitnessConstraint = false; - protected Comparator fitnessConstraintComparator; - - public BaseObjective(String name) { - Objects.requireNonNull(name, "Name of the objective cannot be null."); - this.name = name; - } - - @Override - public String getName() { - return name; - } - - @Override - public void setComparator(Comparator comparator) { - this.comparator = comparator; - } - - @Override - public Comparator getComparator() { - return comparator; - } - - public BaseObjective withComparator(Comparator comparator) { - setComparator(comparator); - return this; - } - - /** - * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the - * current state as a solution. - * - * @param fitnessConstraint - * Solutions should be better than this value. - * @param fitnessConstraintComparator - * {@link Comparator} to determine if the current state is better than the given value. - * @return The actual instance to enable builder pattern like usage. - */ - public BaseObjective withHardConstraintOnFitness(double fitnessConstraint, - Comparator fitnessConstraintComparator) { - this.fitnessConstraint = fitnessConstraint; - this.fitnessConstraintComparator = fitnessConstraintComparator; - this.isThereFitnessConstraint = true; - return this; - } - - /** - * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the - * current state as a solution. The provided comparator will be used. - * - * @param fitnessConstraint - * Solutions should be better than this value. - * @return The actual instance to enable builder pattern like usage. - */ - public BaseObjective withHardConstraintOnFitness(double fitnessConstraint) { - return withHardConstraintOnFitness(fitnessConstraint, null); - } - - @Override - public void init(DesignSpaceExplorationAdapter context) { - if (fitnessConstraintComparator == null) { - fitnessConstraintComparator = comparator; - } - } - - @Override - public boolean isHardObjective() { - return isThereFitnessConstraint; - } - - @Override - public boolean satisfiesHardObjective(Double fitness) { - if (isThereFitnessConstraint) { - int compare = fitnessConstraintComparator.compare(fitness, fitnessConstraint); - if (compare < 0) { - return false; - } - } - return true; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof BaseObjective baseObjective) { - return name.equals(baseObjective.getName()); - } - return false; - } - - @Override - public String toString() { - return name; - } - -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Comparators.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Comparators.java deleted file mode 100644 index 7fba6736..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Comparators.java +++ /dev/null @@ -1,25 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.query.dse.objectives; - -import java.util.Comparator; - -public class Comparators { - - private Comparators() { /*Utility class constructor*/ } - - public static final Comparator HIGHER_IS_BETTER = (o1, o2) -> o1.compareTo(o2); - - public static final Comparator LOWER_IS_BETTER = (o1, o2) -> o2.compareTo(o1); - - private static final Double ZERO = (double) 0; - - public static final Comparator DIFFERENCE_TO_ZERO_IS_BETTER = (o1, o2) -> ZERO.compareTo(Math.abs(o1)-Math.abs(o2)); - -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Fitness.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Fitness.java deleted file mode 100644 index ea80ed55..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Fitness.java +++ /dev/null @@ -1,21 +0,0 @@ -package tools.refinery.store.query.dse.objectives; - -import java.util.HashMap; - -public class Fitness extends HashMap { - - private boolean satisfiesHardObjectives; - - public boolean isSatisfiesHardObjectives() { - return satisfiesHardObjectives; - } - - public void setSatisfiesHardObjectives(boolean satisfiesHardObjectives) { - this.satisfiesHardObjectives = satisfiesHardObjectives; - } - - @Override - public String toString() { - return super.toString() + " hardObjectives=" + satisfiesHardObjectives; - } -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java deleted file mode 100644 index df86f36a..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/Objective.java +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.query.dse.objectives; - -import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; - -import java.util.Comparator; - -/** - * - * Implementation of this interface represents a single objective of the DSE problem, which can assess a solution - * (trajectory) in a single number. It has a name and a comparator which orders two solution based on the calculated - * value. - *

- * Objectives can be either hard or soft objectives. Hard objectives can be satisfied or unsatisfied. If all of the hard - * objectives are satisfied on a single solution, then it is considered to be a valid (or goal) solution. - *

- * Certain objectives can have inner state for calculating the fitness value. In this case a new instance is necessary - * for every new thread, and the {@code createNew} method should not return the same instance more than once. - * - * @author Andras Szabolcs Nagy - * - */ -public interface Objective { - - /** - * Returns the name of the objective. - * - * @return The name of the objective. - */ - String getName(); - - /** - * Sets the {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is to - * minimize or maximize (or minimize or maximize a delta from a given number). - * - * @param comparator The comparator. - */ - void setComparator(Comparator comparator); - - /** - * Returns a {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is - * to minimize or maximize (or minimize or maximize a delta from a given number). - * - * @return The comparator. - */ - Comparator getComparator(); - - /** - * Calculates the value of the objective on a given solution (trajectory). - * - * @param context - * The {@link DesignSpaceExplorationAdapter} - * @return The objective value in double. - */ - Double getFitness(DesignSpaceExplorationAdapter context); - - /** - * Initializes the objective. It is called exactly once for every thread starts. - * - * @param context - * The {@link DesignSpaceExplorationAdapter}. - */ - void init(DesignSpaceExplorationAdapter context); - - /** - * Returns an instance of the {@link Objective}. If it returns the same instance, all the methods has to be thread - * save as they are called concurrently. - * - * @return An instance of the objective. - */ - Objective createNew(); - - /** - * Returns true if the objective is a hard objective. In such a case the method - * {@link Objective#satisfiesHardObjective(Double)} is called. - * - * @return True if the objective is a hard objective. - * @see Objective#satisfiesHardObjective(Double) - * @see Objective - */ - boolean isHardObjective(); - - /** - * Determines if the given fitness value satisfies the hard objective. - * - * @param fitness - * The fitness value of a solution. - * @return True if it satisfies the hard objective or it is a soft constraint. - * @see Objective - */ - boolean satisfiesHardObjective(Double fitness); - -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java deleted file mode 100644 index 0c925096..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/objectives/ObjectiveComparatorHelper.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.query.dse.objectives; - -import java.util.List; - -/** - * This class is responsible to compare and sort fitness values. - * - * @author András Szabolcs Nagy - */ -public class ObjectiveComparatorHelper { - - private final List objectives; - - public ObjectiveComparatorHelper(List objectives) { - this.objectives = objectives; - } - - /** - * Compares two fitnesses based on dominance. Returns -1 if the second parameter {@code o2} is a better - * solution ({@code o2} dominates {@code o1}), 1 if the first parameter {@code o1} is better ({@code o1} dominates - * {@code o2}) and returns 0 if they are non-dominating each other. - */ - public int compare(Fitness o1, Fitness o2) { - - boolean o1HasBetterFitness = false; - boolean o2HasBetterFitness = false; - - for (Objective objective : objectives) { - String objectiveName = objective.getName(); - int sgn = objective.getComparator().compare(o1.get(objectiveName), o2.get(objectiveName)); - - if (sgn < 0) { - o2HasBetterFitness = true; - } - if (sgn > 0) { - o1HasBetterFitness = true; - } - if (o1HasBetterFitness && o2HasBetterFitness) { - break; - } - } - if (o2HasBetterFitness) { - } else if (o1HasBetterFitness) { - return 1; - } - - return 0; - - } -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/BestFirstStrategy.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/BestFirstStrategy.java deleted file mode 100644 index 6264c502..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/BestFirstStrategy.java +++ /dev/null @@ -1,190 +0,0 @@ -package tools.refinery.store.query.dse.strategy; - -import tools.refinery.store.map.Version; -import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; -import tools.refinery.store.query.dse.Strategy; -import tools.refinery.store.query.dse.internal.Activation; -import tools.refinery.store.query.dse.objectives.Fitness; -import tools.refinery.store.query.dse.objectives.ObjectiveComparatorHelper; - -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.PriorityQueue; - -public class BestFirstStrategy implements Strategy { - - private DesignSpaceExplorationAdapter dseAdapter; - - private int maxDepth; - private boolean backTrackIfSolution = true; - private boolean onlyBetterFirst = false; - - private PriorityQueue trajectoriesToExplore; - - private static class TrajectoryWithFitness { - - public List trajectory; - public Fitness fitness; - - public TrajectoryWithFitness(List trajectory, Fitness fitness) { - super(); - this.trajectory = trajectory; - this.fitness = fitness; - } - - @Override - public String toString() { - return trajectory.toString() + fitness.toString(); - } - - } - - public BestFirstStrategy() { - this(-1); - } - - public BestFirstStrategy(int maxDepth) { - if (maxDepth < 0) { - this.maxDepth = Integer.MAX_VALUE; - } else { - this.maxDepth = maxDepth; - } - } - - public BestFirstStrategy continueIfHardObjectivesFulfilled() { - backTrackIfSolution = false; - return this; - } - - public BestFirstStrategy goOnOnlyIfFitnessIsBetter() { - onlyBetterFirst = true; - return this; - } - - @Override - public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { - this.dseAdapter = designSpaceExplorationAdapter; - final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); - - trajectoriesToExplore = new PriorityQueue(11, - (o1, o2) -> objectiveComparatorHelper.compare(o2.fitness, o1.fitness)); - } - - @Override - public void explore() { - final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); - - boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); - if (!globalConstraintsAreSatisfied) { - // "Global constraint is not satisfied in the first state. Terminate."); - return; - } - - final Fitness firstFitness = dseAdapter.calculateFitness(); - if (firstFitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - // "First state is a solution. Terminate."); - if (backTrackIfSolution) { - return; - } - } - - if (maxDepth == 0) { - return; - } - - final List firstTrajectory = dseAdapter.getTrajectory(); - TrajectoryWithFitness currentTrajectoryWithFitness = new TrajectoryWithFitness(firstTrajectory, firstFitness); - trajectoriesToExplore.add(currentTrajectoryWithFitness); - - mainLoop: while (true) { - - if (currentTrajectoryWithFitness == null) { - if (trajectoriesToExplore.isEmpty()) { - // "State space is fully traversed."); - return; - } else { - currentTrajectoryWithFitness = trajectoriesToExplore.element(); -// if (logger.isDebugEnabled()) { -// "New trajectory is chosen: " + currentTrajectoryWithFitness); -// } - dseAdapter.restoreTrajectory(currentTrajectoryWithFitness.trajectory); - } - } - - Collection activations = dseAdapter.getUntraversedActivations(); - Iterator iterator = activations.iterator(); - - - - while (iterator.hasNext()) { - final Activation nextActivation = iterator.next(); - if (!iterator.hasNext()) { - // "Last untraversed activation of the state."); - trajectoriesToExplore.remove(currentTrajectoryWithFitness); - } - -// if (logger.isDebugEnabled()) { -// "Executing new activation: " + nextActivation); -// } - dseAdapter.fireActivation(nextActivation); - if (dseAdapter.isCurrentStateAlreadyTraversed()) { - // "The new state is already visited."); - dseAdapter.backtrack(); - } else if (!dseAdapter.checkGlobalConstraints()) { - // "Global constraint is not satisfied."); - dseAdapter.backtrack(); - } else { - final Fitness nextFitness = dseAdapter.calculateFitness(); - if (nextFitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - // "Found a solution."); - if (backTrackIfSolution) { - dseAdapter.backtrack(); - continue; - } - } - if (dseAdapter.getDepth() >= maxDepth) { - // "Reached max depth."); - dseAdapter.backtrack(); - continue; - } - - TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness( - dseAdapter.getTrajectory(), nextFitness); - trajectoriesToExplore.add(nextTrajectoryWithFitness); - - int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness, - nextTrajectoryWithFitness.fitness); - if (compare < 0) { - // "Better fitness, moving on: " + nextFitness); - currentTrajectoryWithFitness = nextTrajectoryWithFitness; - continue mainLoop; - } else if (compare == 0) { - if (onlyBetterFirst) { - // "Equally good fitness, backtrack: " + nextFitness); - dseAdapter.backtrack(); - continue; - } else { - // "Equally good fitness, moving on: " + nextFitness); - currentTrajectoryWithFitness = nextTrajectoryWithFitness; - continue mainLoop; - } - } else { - // "Worse fitness."); - currentTrajectoryWithFitness = null; - continue mainLoop; - } - } - } - - // "State is fully traversed."); - trajectoriesToExplore.remove(currentTrajectoryWithFitness); - currentTrajectoryWithFitness = null; - - } - // "Interrupted."); - - } -} diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/DepthFirstStrategy.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/DepthFirstStrategy.java deleted file mode 100644 index 8192048a..00000000 --- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/dse/strategy/DepthFirstStrategy.java +++ /dev/null @@ -1,108 +0,0 @@ -package tools.refinery.store.query.dse.strategy; - -import tools.refinery.store.query.dse.DesignSpaceExplorationAdapter; -import tools.refinery.store.query.dse.Strategy; -import tools.refinery.store.query.dse.internal.Activation; -import tools.refinery.store.query.dse.objectives.Fitness; - -import java.util.Collection; - -public class DepthFirstStrategy implements Strategy { - - private DesignSpaceExplorationAdapter dseAdapter; - - private int maxDepth; - private boolean backTrackIfSolution = true; - - public DepthFirstStrategy() { - this(-1); - } - - public DepthFirstStrategy(int maxDepth) { - if (maxDepth < 0) { - this.maxDepth = Integer.MAX_VALUE; - } else { - this.maxDepth = maxDepth; - } - } - - public DepthFirstStrategy continueIfHardObjectivesFulfilled() { - backTrackIfSolution = false; - return this; - } - - @Override - public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { - this.dseAdapter = designSpaceExplorationAdapter; - } - - @Override - public void explore() { - mainloop: while (true) { - var globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); - if (!globalConstraintsAreSatisfied) { - var isSuccessfulUndo = dseAdapter.backtrack(); - if (!isSuccessfulUndo) { -// "Global constraint is not satisfied and cannot backtrack." - break; - } - else { -// "Global constraint is not satisfied, backtrack." - continue; - } - } - - Fitness fitness = dseAdapter.calculateFitness(); - if (fitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - if (backTrackIfSolution) { - var isSuccessfulUndo = dseAdapter.backtrack(); - if (!isSuccessfulUndo) { -// "Found a solution but cannot backtrack." - break; - } else { -// "Found a solution, backtrack." - continue; - } - } - } - - var depth = dseAdapter.getDepth(); - if (dseAdapter.getDepth() >= maxDepth) { - var isSuccessfulUndo = dseAdapter.backtrack(); - if (!isSuccessfulUndo) { -// "Reached max depth but cannot backtrack." - break; - } - } - - Collection activations; - do { - activations = dseAdapter.getUntraversedActivations(); - if (activations.isEmpty()) { - if (!dseAdapter.backtrack()) { - // "No more transitions from current state and cannot backtrack." - break mainloop; - } - else { - // "No more transitions from current state, backtrack." - continue; - } - } - } while (activations.isEmpty()); - - dseAdapter.fireRandomActivation(); -// if (dseAdapter.isCurrentInTrajectory()) { -// if (!dseAdapter.backtrack()) { -//// TODO: throw exception -//// "The new state is present in the trajectory but cannot backtrack. Should never happen!" -// break; -// } -// else { -//// "The new state is already visited in the trajectory, backtrack." -// continue; -// } -// } - } - } -} diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java deleted file mode 100644 index 2effb353..00000000 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/CRAExamplesTest.java +++ /dev/null @@ -1,274 +0,0 @@ -package tools.refinery.store.query.dse; - -import org.junit.jupiter.api.Test; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.query.dse.internal.TransformationRule; -import tools.refinery.store.query.dse.strategy.BestFirstStrategy; -import tools.refinery.store.query.dse.strategy.DepthFirstStrategy; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; -import tools.refinery.store.query.view.AnySymbolView; -import tools.refinery.store.query.view.KeyOnlyView; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.ModelVisualizerAdapter; -import tools.refinery.visualization.internal.FileFormat; - -import java.util.List; - -import static tools.refinery.store.query.literal.Literals.not; - -public class CRAExamplesTest { - private static final Symbol name = Symbol.of("Name", 1, String.class); - -// private static final Symbol classModel = Symbol.of("ClassModel", 1); - private static final Symbol classElement = Symbol.of("ClassElement", 1); -// private static final Symbol feature = Symbol.of("Feature", 1); - private static final Symbol attribute = Symbol.of("Attribute", 1); - private static final Symbol method = Symbol.of("Method", 1); - -// private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); - private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); - private static final Symbol dataDependency = Symbol.of("DataDependency", 2); - private static final Symbol functionalDependency = Symbol.of("FunctionalDependency", 2); - - private static final Symbol features = Symbol.of("Features", 2); - private static final Symbol classes = Symbol.of("Classes", 2); - -// private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); - private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); -// private static final AnySymbolView featureView = new KeyOnlyView<>(feature); - private static final AnySymbolView attributeView = new KeyOnlyView<>(attribute); - private static final AnySymbolView methodView = new KeyOnlyView<>(method); -// private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); - private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); - private static final AnySymbolView dataDependencyView = new KeyOnlyView<>(dataDependency); - private static final AnySymbolView functionalDependencyView = new KeyOnlyView<>(functionalDependency); - private static final AnySymbolView featuresView = new KeyOnlyView<>(features); - private static final AnySymbolView classesView = new KeyOnlyView<>(classes); - - /*Example Transformation rules*/ - private static final RelationalQuery feature = Query.of("Feature", - (builder, f) -> builder.clause( - attributeView.call(f)) - .clause( - methodView.call(f)) - ); - - private static final RelationalQuery assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", - (builder, c, f) -> builder.clause( - classElementView.call(c), -// classesView.call(model, c), - encapsulatesView.call(c, f) - )); - - private static final RelationalQuery assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", - (builder, f, c1) -> builder.clause((c2) -> List.of( -// classModelView.call(model), - feature.call(f), - classElementView.call(c1), -// featuresView.call(model, f), - not(assignFeaturePreconditionHelper.call(c2, f)), - not(encapsulatesView.call(c1, f)) - ))); - - private static final RelationalQuery deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, c) -> builder.clause((f) -> List.of( -// classModelView.call(model), - classElementView.call(c), -// featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - private static final RelationalQuery createClassPreconditionHelper = Query.of("CreateClassPreconditionHelper", - (builder, f, c) -> builder.clause( - classElementView.call(c), -// classesView.call(model, c), - encapsulatesView.call(c, f) - )); - - private static final RelationalQuery createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, f) -> builder.clause((c) -> List.of( -// classModelView.call(model), - feature.call(f), - not(createClassPreconditionHelper.call(f, c)) - ))); - - private static final RelationalQuery moveFeaturePrecondition = Query.of("MoveFeature", - (builder, c1, c2, f) -> builder.clause( -// classModelView.call(model), - classElementView.call(c1), - classElementView.call(c2), - c1.notEquivalent(c2), - feature.call(f), -// classesView.call(model, c1), -// classesView.call(model, c2), -// featuresView.call(model, f), - encapsulatesView.call(c1, f) - )); - - private static final TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", - assignFeaturePrecondition, - (model) -> { -// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); - var encapsulatesInterpretation = model.getInterpretation(encapsulates); - return ((Tuple activation) -> { - var feature = activation.get(0); - var classElement = activation.get(1); - -// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); - encapsulatesInterpretation.put(Tuple.of(classElement, feature), true); - }); - }); - - private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", - deleteEmptyClassPrecondition, - (model) -> { -// var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - // TODO: can we move dseAdapter outside? - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// var modelElement = activation.get(0); - var classElement = activation.get(0); - -// classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - dseAdapter.deleteObject(Tuple.of(classElement)); - }); - }); - - private static final TransformationRule createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classElementInterpretation = model.getInterpretation(classElement); -// var classesInterpretation = model.getInterpretation(classes); - var encapsulatesInterpretation = model.getInterpretation(encapsulates); - return ((Tuple activation) -> { - // TODO: can we move dseAdapter outside? - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// var modelElement = activation.get(0); - var feature = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - classElementInterpretation.put(newClassElement, true); -// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true); - }); - }); - - private static final TransformationRule moveFeatureRule = new TransformationRule("MoveFeature", - moveFeaturePrecondition, - (model) -> { - var encapsulatesInterpretation = model.getInterpretation(encapsulates); - return ((Tuple activation) -> { - var classElement1 = activation.get(0); - var classElement2 = activation.get(1); - var feature = activation.get(2); - - encapsulatesInterpretation.put(Tuple.of(classElement1, feature), false); - encapsulatesInterpretation.put(Tuple.of(classElement2, feature), true); - }); - }); - - @Test - void craTest() { - var store = ModelStore.builder() - .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, - functionalDependency, name) - .with(ViatraModelQueryAdapter.builder() - .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, - deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, - moveFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) -// .strategy(new DepthFirstStrategy(3).continueIfHardObjectivesFulfilled() - .strategy(new BestFirstStrategy(6).continueIfHardObjectivesFulfilled() -// .goOnOnlyIfFitnessIsBetter() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// dseAdapter.setRandom(1); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - -// var modelInterpretation = model.getInterpretation(classModel); - var nameInterpretation = model.getInterpretation(name); - var methodInterpretation = model.getInterpretation(method); - var attributeInterpretation = model.getInterpretation(attribute); - var dataDependencyInterpretation = model.getInterpretation(dataDependency); - var functionalDependencyInterpretation = model.getInterpretation(functionalDependency); - -// var modelElement = dseAdapter.createObject(); - var method1 = dseAdapter.createObject(); - var method1Id = method1.get(0); - var method2 = dseAdapter.createObject(); - var method2Id = method2.get(0); - var method3 = dseAdapter.createObject(); - var method3Id = method3.get(0); - var method4 = dseAdapter.createObject(); - var method4Id = method4.get(0); - var attribute1 = dseAdapter.createObject(); - var attribute1Id = attribute1.get(0); - var attribute2 = dseAdapter.createObject(); - var attribute2Id = attribute2.get(0); - var attribute3 = dseAdapter.createObject(); - var attribute3Id = attribute3.get(0); - var attribute4 = dseAdapter.createObject(); - var attribute4Id = attribute4.get(0); - var attribute5 = dseAdapter.createObject(); - var attribute5Id = attribute5.get(0); - - nameInterpretation.put(method1, "M1"); - nameInterpretation.put(method2, "M2"); - nameInterpretation.put(method3, "M3"); - nameInterpretation.put(method4, "M4"); - nameInterpretation.put(attribute1, "A1"); - nameInterpretation.put(attribute2, "A2"); - nameInterpretation.put(attribute3, "A3"); - nameInterpretation.put(attribute4, "A4"); - nameInterpretation.put(attribute5, "A5"); - - - -// modelInterpretation.put(modelElement, true); - methodInterpretation.put(method1, true); - methodInterpretation.put(method2, true); - methodInterpretation.put(method3, true); - methodInterpretation.put(method4, true); - attributeInterpretation.put(attribute1, true); - attributeInterpretation.put(attribute2, true); - attributeInterpretation.put(attribute3, true); - attributeInterpretation.put(attribute4, true); - attributeInterpretation.put(attribute5, true); - - dataDependencyInterpretation.put(Tuple.of(method1Id, attribute1Id), true); - dataDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); - dataDependencyInterpretation.put(Tuple.of(method2Id, attribute2Id), true); - dataDependencyInterpretation.put(Tuple.of(method3Id, attribute3Id), true); - dataDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); - dataDependencyInterpretation.put(Tuple.of(method4Id, attribute3Id), true); - dataDependencyInterpretation.put(Tuple.of(method4Id, attribute5Id), true); - - functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); - functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute4Id), true); - functionalDependencyInterpretation.put(Tuple.of(method2Id, attribute1Id), true); - functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute1Id), true); - functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); - functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true); - - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - System.out.println("states size: " + states.size()); - System.out.println("states: " + states); - var visualizer = model.getAdapter(ModelVisualizerAdapter.class); - visualizer.renderDesignSpace("test_output", FileFormat.SVG); - } - -} diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java deleted file mode 100644 index 821be7e6..00000000 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DebugTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package tools.refinery.store.query.dse; - -import org.junit.jupiter.api.Test; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.query.dse.internal.TransformationRule; -import tools.refinery.store.query.dse.strategy.BestFirstStrategy; -import tools.refinery.store.query.dse.strategy.DepthFirstStrategy; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; -import tools.refinery.store.query.view.AnySymbolView; -import tools.refinery.store.query.view.KeyOnlyView; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.ModelVisualizerAdapter; -import tools.refinery.visualization.internal.FileFormat; - -public class DebugTest { - private static final Symbol classModel = Symbol.of("ClassModel", 1); - private static final Symbol classElement = Symbol.of("ClassElement", 1); - private static final Symbol feature = Symbol.of("Feature", 1); - - private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); - private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); - - private static final Symbol features = Symbol.of("Features", 2); - private static final Symbol classes = Symbol.of("Classes", 2); - - private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); - private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); - private static final AnySymbolView featureView = new KeyOnlyView<>(feature); - private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); - private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); - private static final AnySymbolView featuresView = new KeyOnlyView<>(features); - private static final AnySymbolView classesView = new KeyOnlyView<>(classes); - - - @Test - void BFSTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() -// .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() -// .goOnOnlyIfFitnessIsBetter() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// dseAdapter.setRandom(1); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var modelElement = dseAdapter.createObject(); - modelElementInterpretation.put(modelElement, true); - classElementInterpretation.put(modelElement, true); - queryEngine.flushChanges(); - - - var states = dseAdapter.explore(); - var visualizer = model.getAdapter(ModelVisualizerAdapter.class); - visualizer.renderDesignSpace("test_output", FileFormat.SVG); - System.out.println("states size: " + states.size()); - System.out.println("states: " + states); - - } -} diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java deleted file mode 100644 index 59775b43..00000000 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/DesignSpaceExplorationTest.java +++ /dev/null @@ -1,487 +0,0 @@ -package tools.refinery.store.query.dse; - -import org.junit.jupiter.api.Test; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.query.dse.internal.TransformationRule; -import tools.refinery.store.query.dse.strategy.BestFirstStrategy; -import tools.refinery.store.query.dse.strategy.DepthFirstStrategy; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; -import tools.refinery.store.query.view.AnySymbolView; -import tools.refinery.store.query.view.KeyOnlyView; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.ModelVisualizerAdapter; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class DesignSpaceExplorationTest { -// private static final Symbol namedElement = Symbol.of("NamedElement", 1); -// private static final Symbol attribute = Symbol.of("Attribute", 1); -// private static final Symbol method = Symbol.of("Method", 1); -// private static final Symbol dataDependency = Symbol.of("DataDependency", 2); -// private static final Symbol functionalDependency = Symbol.of("FunctionalDependency", 2); - - private static final Symbol classModel = Symbol.of("ClassModel", 1); - private static final Symbol classElement = Symbol.of("ClassElement", 1); - private static final Symbol feature = Symbol.of("Feature", 1); - - private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); - private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); - - private static final Symbol features = Symbol.of("Features", 2); - private static final Symbol classes = Symbol.of("Classes", 2); - - private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); - private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); - private static final AnySymbolView featureView = new KeyOnlyView<>(feature); - private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); - private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); - private static final AnySymbolView featuresView = new KeyOnlyView<>(features); - private static final AnySymbolView classesView = new KeyOnlyView<>(classes); - - @Test - void createObjectTest() { - var store = ModelStore.builder() - .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder()) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - assertEquals(0, dseAdapter.getModelSize()); - - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - assertEquals(0, newModelId); - assertEquals(1, newClass1Id); - assertEquals(2, newClass2Id); - assertEquals(3, newFieldId); - assertEquals(4, dseAdapter.getModelSize()); - } - - @Test - void deleteMiddleObjectTest() { - var store = ModelStore.builder() - .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder()) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - assertEquals(0, dseAdapter.getModelSize()); - - var newObject0 = dseAdapter.createObject(); - var newObject0Id = newObject0.get(0); - var newObject1 = dseAdapter.createObject(); - var newObject1Id = newObject1.get(0); - var newObject2 = dseAdapter.createObject(); - var newObject2Id = newObject2.get(0); - var newObject3 = dseAdapter.createObject(); - var newObject3Id = newObject3.get(0); - - assertEquals(0, newObject0Id); - assertEquals(1, newObject1Id); - assertEquals(2, newObject2Id); - assertEquals(3, newObject3Id); - assertEquals(4, dseAdapter.getModelSize()); - - dseAdapter.deleteObject(newObject1); - assertEquals(4, dseAdapter.getModelSize()); - - var newObject4 = dseAdapter.createObject(); - var newObject4Id = newObject4.get(0); - assertEquals(4, newObject4Id); - assertEquals(5, dseAdapter.getModelSize()); - - dseAdapter.deleteObject(newObject4); - assertEquals(5, dseAdapter.getModelSize()); - } - - @Test - void DFSTrivialTest() { - var store = ModelStore.builder() - .symbols(classModel) - .with(ViatraModelQueryAdapter.builder()) - .with(ModelVisualizerAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy(0))) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - assertEquals(0, states.iterator().next()); - } - - @Test - void DFSOneRuleTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(ModelVisualizerAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new DepthFirstStrategy(4) - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - assertEquals(0, states.iterator().next()); - } - - @Test - void DFSContinueTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(ModelVisualizerAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - var iterator = states.iterator(); - assertEquals(5, states.size()); - assertEquals(0, iterator.next()); - assertEquals(1, iterator.next()); - assertEquals(2, iterator.next()); - assertEquals(3, iterator.next()); - assertEquals(4, iterator.next()); - } - - @Test - void DFSCompletenessTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new DepthFirstStrategy(10).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(2047, states.size()); - } - - @Test - void BFSTrivialTest() { - var store = ModelStore.builder() - .symbols(classModel) - .with(ViatraModelQueryAdapter.builder()) - .with(ModelVisualizerAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new BestFirstStrategy(0))) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - assertEquals(0, states.iterator().next()); - } - - @Test - void BFSOneRuleTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(ModelVisualizerAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new BestFirstStrategy(4) - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - assertEquals(0, states.iterator().next()); - } - - @Test - void BFSContinueTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(ModelVisualizerAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - var iterator = states.iterator(); - assertEquals(5, states.size()); - assertEquals(0, iterator.next()); - assertEquals(1, iterator.next()); - assertEquals(2, iterator.next()); - assertEquals(3, iterator.next()); - assertEquals(4, iterator.next()); - } - - @Test - void BFSCompletenessTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new BestFirstStrategy(10).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(2047, states.size()); - } - -} diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/TransformationRuleTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/TransformationRuleTest.java deleted file mode 100644 index 1fb3421b..00000000 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/dse/TransformationRuleTest.java +++ /dev/null @@ -1,403 +0,0 @@ -package tools.refinery.store.query.dse; - -import org.junit.jupiter.api.Test; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.query.dse.internal.TransformationRule; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; -import tools.refinery.store.query.view.AnySymbolView; -import tools.refinery.store.query.view.KeyOnlyView; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; - -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static tools.refinery.store.query.literal.Literals.not; -import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults; - -public class TransformationRuleTest { - - private static final Symbol classModel = Symbol.of("ClassModel", 1); - private static final Symbol classElement = Symbol.of("ClassElement", 1); - private static final Symbol feature = Symbol.of("Feature", 1); - - private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); - private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); - - private static final Symbol features = Symbol.of("Features", 2); - private static final Symbol classes = Symbol.of("Classes", 2); - - private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); - private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); - private static final AnySymbolView featureView = new KeyOnlyView<>(feature); - private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); - private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); - private static final AnySymbolView featuresView = new KeyOnlyView<>(features); - private static final AnySymbolView classesView = new KeyOnlyView<>(classes); - - @Test - void activationsTest() { - var assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", - (builder, model, c, f) -> builder.clause( - classElementView.call(c), - classesView.call(model, c), - encapsulatesView.call(c, f) - )); - - var assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f) - -> builder.clause((model, c1) -> List.of( - classModelView.call(model), - featureView.call(f), - classElementView.call(c2), - featuresView.call(model, f), - classesView.call(model, c1), - not(assignFeaturePreconditionHelper.call(model, c2, f)), - not(encapsulatesView.call(c2, f)) - ))); - - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", - assignFeaturePrecondition, - (model) -> { - var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); - return ((Tuple activation) -> { - var feature = activation.get(0); - var classElement = activation.get(1); - - isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); - }); - }); - - TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }); - - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(assignFeaturePrecondition, assignFeaturePreconditionHelper, - deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder()) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - assignFeatureRule.prepare(model, queryEngine); - deleteEmptyClassRule.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsSets(); - var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsSets(); - - assertResults(Map.of( - Tuple.of(newClass1Id, newFieldId), true, - Tuple.of(newClass2Id, newFieldId), true - ), assignFeatureRuleActivations); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRuleActivations); - } - - @Test - void randomActivationTest() { - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule deleteEmptyClassRule0 = new TransformationRule("DeleteEmptyClass0", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }, - 0L); - - TransformationRule deleteEmptyClassRule1 = new TransformationRule("DeleteEmptyClass1", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }, - 78634L); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder()) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - deleteEmptyClassRule0.prepare(model, queryEngine); - deleteEmptyClassRule1.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - - var activation0 = deleteEmptyClassRule0.getRandomActivation().activation(); - var activation1 = deleteEmptyClassRule1.getRandomActivation().activation(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule0.getAllActivationsAsSets()); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule1.getAllActivationsAsSets()); - - assertEquals(Tuple.of(newModelId, newClass2Id), activation0); - assertEquals(Tuple.of(newModelId, newClass1Id), activation1); - - } - - @Test - void fireTest() { - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder()) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - deleteEmptyClassRule.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsSets()); - - - deleteEmptyClassRule.fireActivation(Tuple.of(0, 1)); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), false, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsSets()); - } - - @Test - void randomFireTest() { - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass0", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }, - 0L); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder()) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - deleteEmptyClassRule.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsSets()); - - deleteEmptyClassRule.fireRandomActivation(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), false - ), deleteEmptyClassRule.getAllActivationsAsSets()); - - deleteEmptyClassRule.fireRandomActivation(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), false, - Tuple.of(newModelId, newClass2Id), false - ), deleteEmptyClassRule.getAllActivationsAsSets()); - - } -} -- cgit v1.2.3-54-g00ecf From 87fe96f3c67993ab1e4b131d73ecf276482e65a9 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Thu, 3 Aug 2023 16:58:25 +0200 Subject: Improve visualization -Display values from all relevant interpretations -Support TruthValue -Add tabular formatting -Add colors --- .../internal/ModelVisualizerAdapterImpl.java | 214 +++++++++++++++++---- 1 file changed, 178 insertions(+), 36 deletions(-) (limited to 'subprojects') diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java index 33c5a43b..8555da5f 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -4,13 +4,14 @@ import tools.refinery.store.map.Version; import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.Model; import tools.refinery.store.representation.AnySymbol; +import tools.refinery.store.representation.TruthValue; import tools.refinery.store.tuple.Tuple; import tools.refinery.visualization.ModelVisualizerAdapter; import tools.refinery.visualization.ModelVisualizerStoreAdapter; import java.io.*; -import java.util.HashMap; -import java.util.Map; +import java.util.*; +import java.util.stream.Collectors; public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { private final Model model; @@ -20,6 +21,15 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { private final Map states = new HashMap<>(); private int transitionCounter = 0; private Integer numberOfStates = 0; + private static final Map truthValueToDot = new HashMap<>() + {{ + put(TruthValue.TRUE, "1"); + put(TruthValue.FALSE, "0"); + put(TruthValue.UNKNOWN, "½"); + put(TruthValue.ERROR, "E"); + put(true, "1"); + put(false, "0"); + }}; public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { this.model = model; @@ -30,22 +40,16 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { if (arity < 1 || arity > 2) { continue; } - var interpretation = model.getInterpretation(symbol); - var valueType = symbol.valueType(); - Interpretation castInterpretation; - if (valueType == Boolean.class) { - castInterpretation = (Interpretation) interpretation; - } - // TODO: support TruthValue -// else if (valueType == TruthValue.class) { -// castInterpretation = (Interpretation) interpretation; -// } - else { - continue; - } - interpretations.put(symbol, castInterpretation); + var interpretation = (Interpretation) model.getInterpretation(symbol); + interpretations.put(symbol, interpretation); } designSpaceBuilder.append("digraph designSpace {\n"); + designSpaceBuilder.append(""" + node[ + style=filled + fillcolor=white + ] + """); } @Override @@ -60,29 +64,165 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { @Override public String createDotForCurrentModelState() { + + var unaryTupleToInterpretationsMap = new HashMap>>(); + var sb = new StringBuilder(); + sb.append("digraph model {\n"); + sb.append(""" + node [ + \tstyle="filled, rounded" + \tshape=plain + \tpencolor="#00000088" + \tfontname="Helvetica" + ] + """); + sb.append(""" + edge [ + \tlabeldistance=3 + \tfontname="Helvetica" + ] + """); + for (var entry : interpretations.entrySet()) { var key = entry.getKey(); var arity = key.arity(); var cursor = entry.getValue().getAll(); - while (cursor.move()) { - if (arity == 1) { - var id = cursor.getKey().get(0); - sb.append("\t").append(id).append(" [label=\"").append(key.name()).append(": ").append(id) - .append("\"]\n"); - } else { - var from = cursor.getKey().get(0); - var to = cursor.getKey().get(1); - sb.append("\t").append(from).append(" -> ").append(to).append(" [label=\"").append(key.name()) - .append("\"]\n"); + if (arity == 1) { + while (cursor.move()) { + unaryTupleToInterpretationsMap.computeIfAbsent(cursor.getKey(), k -> new LinkedHashSet<>()) + .add(entry.getValue()); + } + } else if (arity == 2) { + while (cursor.move()) { + var tuple = cursor.getKey(); + for (var i = 0; i < tuple.getSize(); i++) { + var id = tuple.get(i); + unaryTupleToInterpretationsMap.computeIfAbsent(Tuple.of(id), k -> new LinkedHashSet<>()); + } + sb.append(drawEdge(cursor.getKey(), key, entry.getValue())); } } } + for (var entry : unaryTupleToInterpretationsMap.entrySet()) { + sb.append(drawElement(entry)); + } sb.append("}"); return sb.toString(); } + private StringBuilder drawElement(Map.Entry>> entry) { + var sb = new StringBuilder(); + + var tableStyle = " CELLSPACING=\"0\" BORDER=\"2\" CELLBORDER=\"0\" CELLPADDING=\"4\" STYLE=\"ROUNDED\""; + + var key = entry.getKey(); + var id = key.get(0); + var mainLabel = String.valueOf(id); + var interpretations = entry.getValue(); + var backgroundColor = toBackgroundColorString(averageColor(interpretations)); + + sb.append(id); + sb.append(" [\n"); + sb.append("\tfillcolor=\"").append(backgroundColor).append("\"\n"); + sb.append("\tlabel="); + if (interpretations.isEmpty()) { + sb.append("<\n\t").append(mainLabel).append(""); + } + else { + sb.append("<\n\t\t") + .append(mainLabel).append("\n"); + for (var interpretation : interpretations) { + var rawValue = interpretation.get(key); + + if (rawValue == null || rawValue.equals(TruthValue.FALSE) || rawValue.equals(false)) { + continue; + } + var color = "black"; + if (rawValue.equals(TruthValue.ERROR)) { + color = "red"; + } + var value = truthValueToDot.getOrDefault(rawValue, rawValue.toString()); + var symbol = interpretation.getSymbol(); + + if (symbol.valueType() == String.class) { + value = "\"" + value + "\""; + } + sb.append("\t\t") + .append(interpretation.getSymbol().name()) + .append("") + .append("=").append(value) + .append("\n"); + } + } + sb.append("\t\t>\n"); + sb.append("]\n"); + + return sb; + } + + private String drawEdge(Tuple edge, AnySymbol symbol, Interpretation interpretation) { + var value = interpretation.get(edge); + + if (value == null || value.equals(TruthValue.FALSE) || value.equals(false)) { + return ""; + } + + var sb = new StringBuilder(); + var style = "solid"; + var color = "black"; + if (value.equals(TruthValue.UNKNOWN)) { + style = "dotted"; + } + else if (value.equals(TruthValue.ERROR)) { + style = "dashed"; + color = "red"; + } + + var from = edge.get(0); + var to = edge.get(1); + var name = symbol.name(); + sb.append(from).append(" -> ").append(to) + .append(" [\n\tstyle=").append(style) + .append("\n\tcolor=").append(color) + .append("\n\tfontcolor=").append(color) + .append("\n\tlabel=\"").append(name) + .append("\"]\n"); + return sb.toString(); + } + + private String toBackgroundColorString(Integer[] backgroundColor) { + if (backgroundColor.length == 3) + return String.format("#%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2]); + else if (backgroundColor.length == 4) + return String.format("#%02x%02x%02x%02x", backgroundColor[0], backgroundColor[1], backgroundColor[2], + backgroundColor[3]); + return null; + } + + private Integer[] typePredicateColor(String name) { + var random = new Random(name.hashCode()); + return new Integer[] { random.nextInt(128) + 128, random.nextInt(128) + 128, random.nextInt(128) + 128 }; + } + + private Integer[] averageColor(Set> interpretations) { + if(interpretations.isEmpty()) { + return new Integer[]{256, 256, 256}; + } + // TODO: Only use interpretations where the value is not false (or unknown) + var symbols = interpretations.stream() + .map(i -> typePredicateColor(i.getSymbol().name())).toArray(Integer[][]::new); + + + + return new Integer[] { + Arrays.stream(symbols).map(i -> i[0]).collect(Collectors.averagingInt(Integer::intValue)).intValue(), + Arrays.stream(symbols).map(i -> i[1]).collect(Collectors.averagingInt(Integer::intValue)).intValue(), + Arrays.stream(symbols).map(i -> i[2]).collect(Collectors.averagingInt(Integer::intValue)).intValue() + }; + } + @Override public String createDotForModelState(Version version) { var currentVersion = model.getState(); @@ -129,15 +269,14 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { @Override public void addTransition(Version from, Version to, String action) { - designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)).append(" [label=\"") - .append(transitionCounter++).append(": ").append(action).append("\"]\n"); - + designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) + .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append("\"]\n"); } @Override public void addTransition(Version from, Version to, String action, Tuple activation) { - designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)).append(" [label=\"").append(transitionCounter++) - .append(": ").append(action).append(" / "); + designSpaceBuilder.append(states.get(from)).append(" -> ").append(states.get(to)) + .append(" [label=\"").append(transitionCounter++).append(": ").append(action).append(" / "); for (int i = 0; i < activation.getSize(); i++) { @@ -151,12 +290,16 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { @Override public void addState(Version state) { + if (states.containsKey(state)) { + return; + } states.put(state, numberOfStates++); designSpaceBuilder.append(states.get(state)).append(" [URL=\"./").append(states.get(state)).append(".svg\"]\n"); } @Override public void addSolution(Version state) { + addState(state); designSpaceBuilder.append(states.get(state)).append(" [shape = doublecircle]\n"); } @@ -168,8 +311,8 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { @Override public boolean saveDesignSpace(String path) { saveDot(buildDesignSpaceDot(), path + "/designSpace.dot"); - for (var state : states.keySet()) { - saveDot(createDotForModelState(state), path + "/" + states.get(state) + ".dot"); + for (var entry : states.entrySet()) { + saveDot(createDotForModelState(entry.getKey()), path + "/" + entry.getValue() + ".dot"); } return true; } @@ -182,11 +325,10 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { @Override public boolean renderDesignSpace(String path, FileFormat format) { for (var entry : states.entrySet()) { - var state = entry.getKey(); var stateId = entry.getValue(); - var stateDot = createDotForModelState(state); + var stateDot = createDotForModelState(entry.getKey()); saveDot(stateDot, path + "/" + stateId + ".dot"); - renderDot(stateDot, path + "/" + stateId + "." + format.getFormat()); + renderDot(stateDot, format, path + "/" + stateId + "." + format.getFormat()); } var designSpaceDot = buildDesignSpaceDot(); saveDot(designSpaceDot, path + "/designSpace.dot"); -- cgit v1.2.3-54-g00ecf From cb3a628ee2b95279bb02e3cb0d4cd8b3e7a31842 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Thu, 3 Aug 2023 17:45:24 +0200 Subject: Update test cases for new Version implementation --- .../refinery/store/dse/DesignSpaceExplorationTest.java | 16 ---------------- 1 file changed, 16 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java index c454f4ec..7db2c708 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java @@ -123,7 +123,6 @@ public class DesignSpaceExplorationTest { var states = dseAdapter.explore(); assertEquals(1, states.size()); - assertEquals(0, states.iterator().next()); } @Test @@ -171,7 +170,6 @@ public class DesignSpaceExplorationTest { var states = dseAdapter.explore(); assertEquals(1, states.size()); - assertEquals(0, states.iterator().next()); } @Test @@ -218,13 +216,7 @@ public class DesignSpaceExplorationTest { queryEngine.flushChanges(); var states = dseAdapter.explore(); - var iterator = states.iterator(); assertEquals(5, states.size()); - assertEquals(0, iterator.next()); - assertEquals(1, iterator.next()); - assertEquals(2, iterator.next()); - assertEquals(3, iterator.next()); - assertEquals(4, iterator.next()); } @Test @@ -311,7 +303,6 @@ public class DesignSpaceExplorationTest { var states = dseAdapter.explore(); assertEquals(1, states.size()); - assertEquals(0, states.iterator().next()); } @Test @@ -359,7 +350,6 @@ public class DesignSpaceExplorationTest { var states = dseAdapter.explore(); assertEquals(1, states.size()); - assertEquals(0, states.iterator().next()); } @Test @@ -406,13 +396,7 @@ public class DesignSpaceExplorationTest { queryEngine.flushChanges(); var states = dseAdapter.explore(); - var iterator = states.iterator(); assertEquals(5, states.size()); - assertEquals(0, iterator.next()); - assertEquals(1, iterator.next()); - assertEquals(2, iterator.next()); - assertEquals(3, iterator.next()); - assertEquals(4, iterator.next()); } @Test -- cgit v1.2.3-54-g00ecf From 4220b2af314201714c5a53e856dff8317557ba76 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Thu, 3 Aug 2023 19:16:03 +0200 Subject: Intermediate commit with Lazy NeighbourhoodCalculator and StateEquivalenceChecker prototypes --- .../tools/refinery/store/statecoding/Morphism.java | 11 + .../refinery/store/statecoding/ObjectCode.java | 11 + .../store/statecoding/StateCodeCalculator.java | 2 +- .../store/statecoding/StateCoderAdapter.java | 13 +- .../store/statecoding/StateCoderResult.java | 9 + .../store/statecoding/StateCoderStoreAdapter.java | 3 + .../store/statecoding/StateEquivalenceChecker.java | 22 ++ .../internal/StateCoderAdapterImpl.java | 14 +- .../internal/StateCoderStoreAdapterImpl.java | 29 +++ .../CollectionNeighbourhoodCalculator.java | 131 ++++++++++++ .../neighbourhood/LazyNeighbourhoodCalculator.java | 235 +++++++++++++++++++++ .../neighbourhood/NeighbourhoodCalculator.java | 45 ++-- .../statecoding/neighbourhood/ObjectCode.java | 55 ----- .../statecoding/neighbourhood/ObjectCodeImpl.java | 66 ++++++ .../stateequivalence/CombinationNodePairing.java | 96 +++++++++ .../statecoding/stateequivalence/NodePairing.java | 33 +++ .../stateequivalence/PermutationMorphism.java | 64 ++++++ .../StateEquivalenceCheckerImpl.java | 152 +++++++++++++ .../stateequivalence/TrivialNodePairing.java | 36 ++++ .../store/statecoding/ExperimentalSetupTest.java | 107 ++++++++++ .../store/statecoding/StateCoderBuildTest.java | 80 +++++++ 21 files changed, 1129 insertions(+), 85 deletions(-) create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/Morphism.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/ObjectCode.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderResult.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/CollectionNeighbourhoodCalculator.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCode.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/TrivialNodePairing.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/statecoding/ExperimentalSetupTest.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java (limited to 'subprojects') diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/Morphism.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/Morphism.java new file mode 100644 index 00000000..d4ac3b58 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/Morphism.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +public interface Morphism { + int get(int object); + int getSize(); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/ObjectCode.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/ObjectCode.java new file mode 100644 index 00000000..840253ea --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/ObjectCode.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +public interface ObjectCode { + long get(int object); + int getSize(); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculator.java index 479b61ed..b7f1d81a 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculator.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculator.java @@ -6,5 +6,5 @@ package tools.refinery.store.statecoding; public interface StateCodeCalculator { - + StateCoderResult calculateCodes(); } 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 8795fb68..8cfd24d5 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 @@ -6,7 +6,18 @@ package tools.refinery.store.statecoding; import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.statecoding.internal.StateCoderBuilderImpl; public interface StateCoderAdapter extends ModelAdapter { - int calculateHashCode(); + StateCoderResult calculateStateCode(); + default int calculateModelCode() { + return calculateStateCode().modelCode(); + } + default ObjectCode calculateObjectCode() { + return calculateStateCode().objectCode(); + } + + static StateCoderBuilderImpl builder() { + return new StateCoderBuilderImpl(); + } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderResult.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderResult.java new file mode 100644 index 00000000..d2aff836 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderResult.java @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +public record StateCoderResult(int modelCode, ObjectCode objectCode) { +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderStoreAdapter.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderStoreAdapter.java index 5946a162..c6509521 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderStoreAdapter.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderStoreAdapter.java @@ -6,9 +6,12 @@ package tools.refinery.store.statecoding; import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.map.Version; import tools.refinery.store.model.Model; public interface StateCoderStoreAdapter extends ModelStoreAdapter { + StateEquivalenceChecker.EquivalenceResult checkEquivalence(Version v1, Version v2); + @Override StateCoderAdapter createModelAdapter(Model model); } 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 new file mode 100644 index 00000000..6d8dc6c7 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateEquivalenceChecker.java @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +import tools.refinery.store.model.Interpretation; + +import java.util.List; + +public interface StateEquivalenceChecker { + enum EquivalenceResult { + ISOMORPHIC, UNKNOWN, DIFFERENT + } + + EquivalenceResult constructMorphism( + List> interpretations1, + ObjectCode code1, List> interpretations2, + ObjectCode code2); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java index 689db2e3..b66fc86d 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java @@ -9,8 +9,10 @@ import tools.refinery.store.adapter.ModelStoreAdapter; import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.Model; import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.StateCodeCalculator; import tools.refinery.store.statecoding.StateCoderAdapter; -import tools.refinery.store.statecoding.neighbourhood.NeighbourhoodCalculator; +import tools.refinery.store.statecoding.StateCoderResult; +import tools.refinery.store.statecoding.neighbourhood.LazyNeighbourhoodCalculator; import java.util.Collection; import java.util.List; @@ -18,14 +20,14 @@ import java.util.List; public class StateCoderAdapterImpl implements StateCoderAdapter { final ModelStoreAdapter storeAdapter; final Model model; - final NeighbourhoodCalculator calculator; + final StateCodeCalculator calculator; StateCoderAdapterImpl(ModelStoreAdapter storeAdapter, Model model, Collection> symbols) { this.storeAdapter = storeAdapter; this.model = model; List> interpretations = symbols.stream().map(model::getInterpretation).toList(); - calculator = new NeighbourhoodCalculator(interpretations); + calculator = new LazyNeighbourhoodCalculator(interpretations); } @Override @@ -39,9 +41,7 @@ public class StateCoderAdapterImpl implements StateCoderAdapter { } @Override - public int calculateHashCode() { - return calculator.calculate(); + public StateCoderResult calculateStateCode() { + return calculator.calculateCodes(); } - - } 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 77d36e96..5374755d 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 @@ -5,18 +5,24 @@ */ package tools.refinery.store.statecoding.internal; +import tools.refinery.store.map.Version; import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.Symbol; import tools.refinery.store.statecoding.StateCoderAdapter; import tools.refinery.store.statecoding.StateCoderStoreAdapter; +import tools.refinery.store.statecoding.StateEquivalenceChecker; +import tools.refinery.store.statecoding.stateequivalence.StateEquivalenceCheckerImpl; import java.util.Collection; +import java.util.Objects; public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { final ModelStore store; final Collection> symbols; + final StateEquivalenceChecker equivalenceChecker = new StateEquivalenceCheckerImpl(); + StateCoderStoreAdapterImpl(ModelStore store, Collection> symbols) { this.store = store; this.symbols = symbols; @@ -27,8 +33,31 @@ public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { return store; } + @Override + public StateEquivalenceChecker.EquivalenceResult checkEquivalence(Version v1, Version v2) { + if(Objects.equals(v1,v2)) { + return StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC; + } + var model1 = this.getStore().createModelForState(v1); + var model2 = this.getStore().createModelForState(v2); + + var s1 = model1.getAdapter(StateCoderAdapter.class).calculateStateCode(); + var s2 = model2.getAdapter(StateCoderAdapter.class).calculateStateCode(); + + if(s1.modelCode() != s2.modelCode()) { + return StateEquivalenceChecker.EquivalenceResult.DIFFERENT; + } + + var i1 = symbols.stream().map(model1::getInterpretation).toList(); + var i2 = symbols.stream().map(model2::getInterpretation).toList(); + + return equivalenceChecker.constructMorphism(i1,s1.objectCode(),i2,s2.objectCode()); + } + @Override public StateCoderAdapter createModelAdapter(Model model) { return new StateCoderAdapterImpl(this,model,symbols); } + + } diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/CollectionNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/CollectionNeighbourhoodCalculator.java new file mode 100644 index 00000000..058750ee --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/CollectionNeighbourhoodCalculator.java @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.neighbourhood; + +import org.eclipse.collections.api.set.primitive.MutableLongSet; +import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.statecoding.StateCodeCalculator; +import tools.refinery.store.statecoding.StateCoderResult; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple0; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Random; + +public class CollectionNeighbourhoodCalculator implements StateCodeCalculator { + protected final List> nullImpactValues; + protected final LinkedHashMap, long[]> impactValues; + + public CollectionNeighbourhoodCalculator(List> interpretations) { + this.nullImpactValues = new ArrayList<>(); + this.impactValues = new LinkedHashMap<>(); + Random random = new Random(1); + + for (Interpretation interpretation : interpretations) { + int arity = interpretation.getSymbol().arity(); + if (arity == 0) { + nullImpactValues.add(interpretation); + } else { + long[] impact = new long[arity]; + for (int i = 0; i < arity; i++) { + impact[i] = random.nextLong(); + } + impactValues.put(interpretation, impact); + } + } + } + + @Override + public StateCoderResult calculateCodes() { + ObjectCodeImpl previous = new ObjectCodeImpl(); + ObjectCodeImpl next = new ObjectCodeImpl(); + + int previousSize = 1; + long lastSum; + boolean grows; + + do{ + for (var impactValueEntry : this.impactValues.entrySet()) { + Interpretation interpretation = impactValueEntry.getKey(); + long[] impact = impactValueEntry.getValue(); + var cursor = interpretation.getAll(); + while (cursor.move()) { + Tuple tuple = cursor.getKey(); + Object value = cursor.getValue(); + long tupleHash = getTupleHash(tuple, value, previous); + addHash(next, tuple, impact, tupleHash); + } + } + + previous = next; + next = null; + lastSum = 0; + MutableLongSet codes = new LongHashSet(); + for (int i = 0; i < previous.getSize(); i++) { + long objectHash = previous.get(i); + codes.add(objectHash); + + final long shifted1 = objectHash>>> 32; + final long shifted2 = objectHash << 32; + lastSum += shifted1 + shifted2; + } + int nextSize = codes.size(); + grows = previousSize < nextSize; + previousSize = nextSize; + + if(grows) { + next = new ObjectCodeImpl(previous); + } + } while (grows); + + long result = 1; + for (var nullImpactValue : nullImpactValues) { + result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); + } + result += lastSum; + + return new StateCoderResult((int) result, previous); + } + + protected long getTupleHash(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { + long result = value.hashCode(); + int arity = tuple.getSize(); + if (arity == 1) { + result = result * 31 + objectCodeImpl.get(tuple.get(0)); + } else if (arity == 2) { + result = result * 31 + objectCodeImpl.get(tuple.get(0)); + result = result * 31 + objectCodeImpl.get(tuple.get(1)); + if (tuple.get(0) == tuple.get(1)) { + result++; + } + } else if (arity > 2) { + for (int i = 0; i < arity; i++) { + result = result * 31 + objectCodeImpl.get(tuple.get(i)); + } + } + return result; + } + + protected void addHash(ObjectCodeImpl objectCodeImpl, Tuple tuple, long[] impact, long tupleHashCode) { + if (tuple.getSize() == 1) { + addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); + } else if (tuple.getSize() == 2) { + addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); + addHash(objectCodeImpl, tuple.get(1), impact[1], tupleHashCode); + } else if (tuple.getSize() > 2) { + for (int i = 0; i < tuple.getSize(); i++) { + addHash(objectCodeImpl, tuple.get(i), impact[i], tupleHashCode); + } + } + } + + protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { + objectCodeImpl.set(o, objectCodeImpl.get(o) + tupleHash * impact); + } +} 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 new file mode 100644 index 00000000..79317679 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculator.java @@ -0,0 +1,235 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.neighbourhood; + +import org.eclipse.collections.api.map.primitive.LongIntMap; +import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; +import tools.refinery.store.map.Cursor; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.statecoding.StateCodeCalculator; +import tools.refinery.store.statecoding.StateCoderResult; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple0; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Random; + +public class LazyNeighbourhoodCalculator implements StateCodeCalculator { + protected final List> nullImpactValues; + protected final LinkedHashMap, long[]> impactValues; + + public LazyNeighbourhoodCalculator(List> interpretations) { + this.nullImpactValues = new ArrayList<>(); + this.impactValues = new LinkedHashMap<>(); + Random random = new Random(1); + + for (Interpretation interpretation : interpretations) { + int arity = interpretation.getSymbol().arity(); + if (arity == 0) { + nullImpactValues.add(interpretation); + } else { + long[] impact = new long[arity]; + for (int i = 0; i < arity; i++) { + impact[i] = random.nextInt(); + } + impactValues.put(interpretation, impact); + } + } + } + + public StateCoderResult calculateCodes() { + ObjectCodeImpl previous = new ObjectCodeImpl(); + ObjectCodeImpl next = new ObjectCodeImpl(); + LongIntMap hash2Amount = new LongIntHashMap(); + + long lastSum; + int lastSize = 1; + boolean grows; + + do { + constructNextObjectCodes(previous, next, hash2Amount); + + LongIntHashMap nextHash2Amount = new LongIntHashMap(); + lastSum = calculateLastSum(previous, next, hash2Amount, nextHash2Amount); + + previous = next; + next = null; + + int nextSize = nextHash2Amount.size(); + grows = nextSize > lastSize; + lastSize = nextSize; + + if (grows) { + next = new ObjectCodeImpl(previous); + } + } while (grows); + + long result = 1; + for (var nullImpactValue : nullImpactValues) { + result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); + } + result += lastSum; + + return new StateCoderResult((int) result, previous); + } + + private long calculateLastSum(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount, + LongIntHashMap nextHash2Amount) { + long lastSum = 0; + for (int i = 0; i < next.getSize(); i++) { + final long hash; + if (isUnique(hash2Amount, previous, i)) { + hash = previous.get(i); + next.set(i, hash); + } else { + hash = next.get(i); + } + + final int amount = nextHash2Amount.get(hash); + nextHash2Amount.put(hash, amount + 1); + + final long shifted1 = hash >>> 8; + final long shifted2 = hash << 8; + final long shifted3 = hash >> 2; + lastSum += shifted1*shifted3 + shifted2; + } + return lastSum; + } + + private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount) { + for (var impactValueEntry : this.impactValues.entrySet()) { + Interpretation interpretation = impactValueEntry.getKey(); + var cursor = interpretation.getAll(); + int arity = interpretation.getSymbol().arity(); + long[] impactValue = impactValueEntry.getValue(); + + if (arity == 1) { + while (cursor.move()) { + lazyImpactCalculation1(hash2Amount, previous, next, impactValue, cursor); + } + } else if (arity == 2) { + while (cursor.move()) { + lazyImpactCalculation2(hash2Amount, previous, next, impactValue, cursor); + } + } else { + while (cursor.move()) { + lazyImpactCalculationN(hash2Amount, previous, next, impactValue, cursor); + } + } + } + } + + private boolean isUnique(LongIntMap hash2Amount, ObjectCodeImpl objectCodeImpl, int object) { + final long hash = objectCodeImpl.get(object); + if(hash == 0) { + return false; + } + final int amount = hash2Amount.get(hash); + return amount == 1; + } + + private void lazyImpactCalculation1(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor cursor) { + + Tuple tuple = cursor.getKey(); + int o = tuple.get(0); + + if (isUnique(hash2Amount, previous, o)) { + next.ensureSize(o); + } else { + Object value = cursor.getValue(); + long tupleHash = getTupleHash1(tuple, value, previous); + + addHash(next, o, impactValues[0], tupleHash); + } + } + + private void lazyImpactCalculation2(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor cursor) { + Tuple tuple = cursor.getKey(); + int o1 = tuple.get(0); + int o2 = tuple.get(1); + + boolean u1 = isUnique(hash2Amount, previous, o1); + boolean u2 = isUnique(hash2Amount, previous, o2); + + if (u1 && u2) { + next.ensureSize(o1); + next.ensureSize(o2); + } else { + Object value = cursor.getValue(); + long tupleHash = getTupleHash2(tuple, value, previous); + + if (!u1) { + addHash(next, o1, impactValues[0], tupleHash); + next.ensureSize(o2); + } + if (!u2) { + next.ensureSize(o1); + addHash(next, o2, impactValues[1], tupleHash); + } + } + } + + private void lazyImpactCalculationN(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor cursor) { + Tuple tuple = cursor.getKey(); + + boolean[] uniques = new boolean[tuple.getSize()]; + boolean allUnique = true; + for (int i = 0; i < tuple.getSize(); i++) { + final boolean isUnique = isUnique(hash2Amount, previous, tuple.get(i)); + uniques[i] = isUnique; + allUnique &= isUnique; + } + + if (allUnique) { + for (int i = 0; i < tuple.getSize(); i++) { + next.ensureSize(tuple.get(i)); + } + } else { + Object value = cursor.getValue(); + long tupleHash = getTupleHashN(tuple, value, previous); + + for (int i = 0; i < tuple.getSize(); i++) { + int o = tuple.get(i); + if (!uniques[i]) { + addHash(next, o, impactValues[i], tupleHash); + } else { + next.ensureSize(o); + } + } + } + } + + private long getTupleHash1(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { + long result = value.hashCode(); + result = result * 31 + objectCodeImpl.get(tuple.get(0)); + return result; + } + + private long getTupleHash2(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { + long result = value.hashCode(); + result = result * 31 + objectCodeImpl.get(tuple.get(0)); + result = result * 31 + objectCodeImpl.get(tuple.get(1)); + if (tuple.get(0) == tuple.get(1)) { + result*=31; + } + return result; + } + + private long getTupleHashN(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { + long result = value.hashCode(); + for (int i = 0; i < tuple.getSize(); i++) { + result = result * 31 + objectCodeImpl.get(tuple.get(i)); + } + return result; + } + + protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { + long x = tupleHash * impact; + objectCodeImpl.set(o, objectCodeImpl.get(o) + x); + } +} 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 24a7122e..212291c3 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,8 @@ package tools.refinery.store.statecoding.neighbourhood; import org.eclipse.collections.api.set.primitive.MutableLongSet; import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; import tools.refinery.store.model.Interpretation; +import tools.refinery.store.statecoding.StateCodeCalculator; +import tools.refinery.store.statecoding.StateCoderResult; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple0; @@ -16,9 +18,9 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Random; -public class NeighbourhoodCalculator { - final List> nullImpactValues; - final LinkedHashMap, long[]> impactValues; +public class NeighbourhoodCalculator implements StateCodeCalculator { + protected final List> nullImpactValues; + protected final LinkedHashMap, long[]> impactValues; public NeighbourhoodCalculator(List> interpretations) { this.nullImpactValues = new ArrayList<>(); @@ -39,9 +41,10 @@ public class NeighbourhoodCalculator { } } - public int calculate() { - ObjectCode previous = new ObjectCode(); - ObjectCode next = new ObjectCode(); + @Override + public StateCoderResult calculateCodes() { + ObjectCodeImpl previous = new ObjectCodeImpl(); + ObjectCodeImpl next = new ObjectCodeImpl(); int previousSize = 1; long lastSum; @@ -77,7 +80,7 @@ public class NeighbourhoodCalculator { previousSize = nextSize; if(grows) { - next = new ObjectCode(previous); + next = new ObjectCodeImpl(previous); } } while (grows); @@ -87,43 +90,43 @@ public class NeighbourhoodCalculator { } result += lastSum; - return (int) result; + return new StateCoderResult((int) result, previous); } - protected long getTupleHash(Tuple tuple, Object value, ObjectCode objectCode) { - long result = (long) value; + protected long getTupleHash(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { + long result = value.hashCode(); int arity = tuple.getSize(); if (arity == 1) { - result = result * 31 + objectCode.get(tuple.get(0)); + result = result * 31 + objectCodeImpl.get(tuple.get(0)); } else if (arity == 2) { - result = result * 31 + objectCode.get(tuple.get(0)); - result = result * 31 + objectCode.get(tuple.get(1)); + result = result * 31 + objectCodeImpl.get(tuple.get(0)); + result = result * 31 + objectCodeImpl.get(tuple.get(1)); if (tuple.get(0) == tuple.get(1)) { result++; } } else if (arity > 2) { for (int i = 0; i < arity; i++) { - result = result * 31 + objectCode.get(tuple.get(i)); + result = result * 31 + objectCodeImpl.get(tuple.get(i)); } } return result; } - protected void addHash(ObjectCode objectCode, Tuple tuple, long[] impact, long tupleHashCode) { + protected void addHash(ObjectCodeImpl objectCodeImpl, Tuple tuple, long[] impact, long tupleHashCode) { if (tuple.getSize() == 1) { - addHash(objectCode, tuple.get(0), impact[0], tupleHashCode); + addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); } else if (tuple.getSize() == 2) { - addHash(objectCode, tuple.get(0), impact[0], tupleHashCode); - addHash(objectCode, tuple.get(1), impact[1], tupleHashCode); + addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); + addHash(objectCodeImpl, tuple.get(1), impact[1], tupleHashCode); } else if (tuple.getSize() > 2) { for (int i = 0; i < tuple.getSize(); i++) { - addHash(objectCode, tuple.get(i), impact[i], tupleHashCode); + addHash(objectCodeImpl, tuple.get(i), impact[i], tupleHashCode); } } } - protected void addHash(ObjectCode objectCode, int o, long impact, long tupleHash) { - objectCode.set(o, objectCode.get(o) + tupleHash * impact); + protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { + objectCodeImpl.set(o, objectCodeImpl.get(o) + tupleHash * impact); } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCode.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCode.java deleted file mode 100644 index 594d2b3a..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCode.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.statecoding.neighbourhood; - -public class ObjectCode { - private long[] vector; - private int size; - - public ObjectCode() { - vector = new long[10]; - size = 0; - } - - public ObjectCode(ObjectCode sameSize) { - this.vector = new long[sameSize.size]; - this.size = sameSize.size; - } - - private void ensureSize(int object) { - if(object >= size) { - size = object+1; - } - - if(object >= vector.length) { - int newLength = vector.length*2; - while(object >= newLength) { - newLength*=2; - } - - long[] newVector = new long[newLength]; - System.arraycopy(vector, 0, newVector, 0, vector.length); - this.vector = newVector; - } - } - - public long get(int object) { - if(object < vector.length) { - return vector[object]; - } else { - return 0; - } - } - - public void set(int object, long value) { - ensureSize(object); - vector[object]=value; - } - - public int getSize() { - return this.size; - } -} 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 new file mode 100644 index 00000000..08e3a90b --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/ObjectCodeImpl.java @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.neighbourhood; + +import tools.refinery.store.statecoding.ObjectCode; + +import java.util.Arrays; + +public class ObjectCodeImpl implements ObjectCode { + private long[] vector; + private int size; + + public ObjectCodeImpl() { + vector = new long[10]; + size = 0; + } + + public ObjectCodeImpl(ObjectCodeImpl sameSize) { + this.vector = new long[sameSize.size]; + this.size = sameSize.size; + } + + public void ensureSize(int object) { + if(object >= size) { + size = object+1; + } + + if(object >= vector.length) { + int newLength = vector.length*2; + while(object >= newLength) { + newLength*=2; + } + + long[] newVector = new long[newLength]; + System.arraycopy(vector, 0, newVector, 0, vector.length); + this.vector = newVector; + } + } + + public long get(int object) { + if(object < vector.length) { + return vector[object]; + } else { + return 0; + } + } + + public void set(int object, long value) { + ensureSize(object); + vector[object]=value; + } + + public int getSize() { + return this.size; + } + + @Override + public String toString() { + return "ObjectCodeImpl{" + + "vector=" + Arrays.toString(Arrays.copyOf(vector,this.size)) + + '}'; + } +} 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 new file mode 100644 index 00000000..2877bd0f --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/CombinationNodePairing.java @@ -0,0 +1,96 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.stateequivalence; + +import org.eclipse.collections.api.factory.primitive.IntIntMaps; +import org.eclipse.collections.api.map.primitive.IntIntMap; +import org.eclipse.collections.api.set.primitive.IntSet; +import org.eclipse.collections.impl.list.Interval; +import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CombinationNodePairing implements NodePairing { + private final int[] left; + private final int[] right; + + CombinationNodePairing(IntSet left, IntHashSet right) { + this.left = left.toArray(); + this.right = right.toArray(); + + Arrays.sort(this.left); + Arrays.sort(this.right); + } + + @Override + public int size() { + return left.length; + } + + static final int LIMIT = 5; + static final List> permutations = new ArrayList<>(); + + /** + * Generates and stores permutations up to a given size. If the number would be more than a limit, it provides a + * single permutation only. + * + * @param max The max number in the permutation + * @return A complete list of permutations of numbers 0...max, or a single permutation. + */ + public static List getPermutations(int max) { + if (max < permutations.size()) { + return permutations.get(max); + } + if (max == 0) { + List result = new ArrayList<>(); + result.add(new int[1]); + permutations.add(result); + return result; + } + List result = new ArrayList<>(); + List previousPermutations = getPermutations(max - 1); + for (var permutation : previousPermutations) { + for (int pos = 0; pos <= max; pos++) { + int[] newPermutation = new int[max + 1]; + if (pos >= 0) + System.arraycopy(permutation, 0, newPermutation, 0, pos); + newPermutation[pos] = max; + if (max - (pos + 1) >= 0) + System.arraycopy(permutation, pos + 1, newPermutation, pos + 1 + 1, max - (pos + 1)); + result.add(newPermutation); + } + } + permutations.add(result); + return result; + } + + @Override + public List permutations() { + final var interval = Interval.zeroTo(this.size() - 1); + + if (isComplete()) { + final List p = getPermutations(this.size() - 1); + return p.stream().map(x -> constructPermutationMap(interval, x)).toList(); + } else { + return List.of(constructTrivialMap(interval)); + } + } + + private IntIntMap constructTrivialMap(Interval interval) { + return IntIntMaps.immutable.from(interval, l -> left[l], r -> right[r]); + } + + private IntIntMap constructPermutationMap(Interval interval, int[] permutation) { + return IntIntMaps.immutable.from(interval, l -> left[l], r -> right[permutation[r]]); + } + + @Override + public boolean isComplete() { + return this.size() <= LIMIT; + } +} 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 new file mode 100644 index 00000000..7e5db7a3 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/NodePairing.java @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.stateequivalence; + +import org.eclipse.collections.api.map.primitive.IntIntMap; +import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; + +import java.util.List; + +public interface NodePairing { + + int size(); + List permutations(); + + boolean isComplete(); + + static NodePairing constructNodePairing(IntHashSet left, IntHashSet right){ + if(left.size() != right.size()) { + return null; + } + + if(left.size() == 1) { + int leftValue = left.intIterator().next(); + int rightValue = right.intIterator().next(); + return new TrivialNodePairing(leftValue, rightValue); + } else { + return new CombinationNodePairing(left,right); + } + } +} 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 new file mode 100644 index 00000000..6be0f3e7 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/PermutationMorphism.java @@ -0,0 +1,64 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.stateequivalence; + +import org.eclipse.collections.api.map.primitive.IntIntMap; +import tools.refinery.store.statecoding.Morphism; + +import java.util.List; + +public class PermutationMorphism implements Morphism { + private final IntIntMap object2PermutationGroup; + private final List> permutationsGroups; + private final int[] selection; + private boolean hasNext; + + PermutationMorphism(IntIntMap object2PermutationGroup, + List> permutationsGroups) { + this.object2PermutationGroup = object2PermutationGroup; + this.permutationsGroups = permutationsGroups; + + this.selection = new int[this.permutationsGroups.size()]; + this.hasNext = true; + } + + public boolean next() { + return next(0); + } + + private boolean next(int position) { + if (position >= permutationsGroups.size()) { + this.hasNext = false; + return false; + } + if (selection[position] + 1 < permutationsGroups.get(position).size()) { + selection[position] = selection[position] + 1; + return true; + } else { + selection[position] = 0; + return next(position + 1); + } + } + + @Override + public int get(int object) { + if(!hasNext) { + throw new IllegalArgumentException("No next permutation!"); + } + + final int groupIndex = object2PermutationGroup.get(object); + final var selectedGroup = permutationsGroups.get(groupIndex); + final int permutationIndex = selection[groupIndex]; + final var selectedPermutation = selectedGroup.get(permutationIndex); + + return selectedPermutation.get(object); + } + + @Override + public int getSize() { + return object2PermutationGroup.size(); + } +} 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 new file mode 100644 index 00000000..fd704086 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/StateEquivalenceCheckerImpl.java @@ -0,0 +1,152 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.stateequivalence; + +import org.eclipse.collections.api.map.primitive.IntIntMap; +import org.eclipse.collections.impl.map.mutable.primitive.IntIntHashMap; +import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; +import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.statecoding.Morphism; +import tools.refinery.store.statecoding.ObjectCode; +import tools.refinery.store.statecoding.StateEquivalenceChecker; +import tools.refinery.store.tuple.Tuple; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { + public static final int LIMIT = 1000; + + @Override + public EquivalenceResult constructMorphism(List> interpretations1, + ObjectCode code1, + List> interpretations2, + ObjectCode code2) { + if (code1.getSize() != code2.getSize()) { + return EquivalenceResult.DIFFERENT; + } + + IntIntHashMap object2PermutationGroup = new IntIntHashMap(); + List> permutationsGroups = new ArrayList<>(); + + final EquivalenceResult permutations = constructPermutationNavigation(indexByHash(code1), indexByHash(code2), + object2PermutationGroup, permutationsGroups); + + if (permutations == EquivalenceResult.DIFFERENT) { + return EquivalenceResult.DIFFERENT; + } + + boolean hasNext; + PermutationMorphism morphism = new PermutationMorphism(object2PermutationGroup, permutationsGroups); + int tried = 0; + do { + if (testMorphism(interpretations1, interpretations2, morphism)) { + return permutations; + } + + if(tried >= LIMIT) { + return EquivalenceResult.UNKNOWN; + } + + hasNext = morphism.next(); + tried++; + } while (hasNext); + + return EquivalenceResult.DIFFERENT; + } + + private LongObjectHashMap indexByHash(ObjectCode code) { + LongObjectHashMap result = new LongObjectHashMap<>(); + for (int o = 0; o < code.getSize(); o++) { + long hash = code.get(o); + var equivalenceClass = result.get(hash); + if (equivalenceClass == null) { + equivalenceClass = new IntHashSet(); + result.put(hash, equivalenceClass); + } + equivalenceClass.add(o); + } + return result; + } + + private EquivalenceResult constructPermutationNavigation(LongObjectHashMap map1, + LongObjectHashMap map2, + IntIntHashMap emptyMapToListOfOptions, + List> emptyListOfOptions) { + if (map1.size() != map2.size()) { + return EquivalenceResult.DIFFERENT; + } + + var iterator = map1.keySet().longIterator(); + + boolean allComplete = true; + + while (iterator.hasNext()) { + long hash = iterator.next(); + var set1 = map1.get(hash); + var set2 = map2.get(hash); + if (set2 == null) { + return EquivalenceResult.DIFFERENT; + } + + var pairing = NodePairing.constructNodePairing(set1, set2); + if (pairing == null) { + return EquivalenceResult.DIFFERENT; + } + + allComplete &= pairing.isComplete(); + + final int optionIndex = emptyListOfOptions.size(); + set1.forEach(key -> emptyMapToListOfOptions.put(key, optionIndex)); + emptyListOfOptions.add(pairing.permutations()); + } + if(allComplete) { + return EquivalenceResult.ISOMORPHIC; + } else { + return EquivalenceResult.UNKNOWN; + } + } + + private boolean testMorphism(List> s, List> t, Morphism m) { + for (int interpretationIndex = 0; interpretationIndex < s.size(); interpretationIndex++) { + var sI = s.get(interpretationIndex); + var tI = t.get(interpretationIndex); + + var cursor = sI.getAll(); + while (cursor.move()) { + final Tuple sTuple = cursor.getKey(); + final Object sValue = cursor.getValue(); + + final Tuple tTuple = apply(sTuple, m); + final Object tValue = tI.get(tTuple); + + if (!Objects.equals(sValue, tValue)) { + return false; + } + } + } + return true; + } + + private Tuple apply(Tuple t, Morphism m) { + final int arity = t.getSize(); + if (arity == 0) { + return Tuple.of(); + } else if (arity == 1) { + return Tuple.of(m.get(t.get(0))); + } else if (arity == 2) { + return Tuple.of(m.get(t.get(0)), m.get(t.get(1))); + } else { + int[] newTupleIndices = new int[arity]; + for (int i = 0; i < arity; i++) { + newTupleIndices[i] = m.get(t.get(i)); + } + return Tuple.of(newTupleIndices); + } + } +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/TrivialNodePairing.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/TrivialNodePairing.java new file mode 100644 index 00000000..f5eadfb9 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/stateequivalence/TrivialNodePairing.java @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.stateequivalence; + +import org.eclipse.collections.api.factory.primitive.IntIntMaps; +import org.eclipse.collections.api.map.primitive.IntIntMap; + +import java.util.List; + +public class TrivialNodePairing implements NodePairing { + final int left; + final int right; + + TrivialNodePairing(int left, int right) { + this.left = left; + this.right = right; + } + + @Override + public int size() { + return 1; + } + + @Override + public List permutations() { + return List.of(IntIntMaps.immutable.of(left,right)); + } + + @Override + public boolean isComplete() { + return true; + } +} diff --git a/subprojects/store/src/test/java/tools/refinery/store/statecoding/ExperimentalSetupTest.java b/subprojects/store/src/test/java/tools/refinery/store/statecoding/ExperimentalSetupTest.java new file mode 100644 index 00000000..87b1623c --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/ExperimentalSetupTest.java @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.map.Version; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; + +import java.util.*; + +class ExperimentalSetupTest { + public static void generate(int size) { + Symbol person = new Symbol<>("Person", 1, Boolean.class, false); + Symbol friend = new Symbol<>("friend", 2, Boolean.class, false); + + var store = ModelStore.builder() + .symbols(person, friend) + .with(StateCoderAdapter + .builder()) + .build(); + + Set versions = new HashSet<>(); + Map> codes = new HashMap<>(); + + var empty = store.createEmptyModel(); + var pI = empty.getInterpretation(person); + + for (int i = 0; i < size; i++) { + pI.put(Tuple.of(i), true); + } + + var emptyVersion = empty.commit(); + versions.add(emptyVersion); + var emptyCode = empty.getAdapter(StateCoderAdapter.class).calculateModelCode(); + List emptyList = new ArrayList<>(); + emptyList.add(emptyVersion); + codes.put(emptyCode, emptyList); + + var storeAdapter = store.getAdapter(StateCoderStoreAdapter.class); + + int dif = 0; + int iso = 0; + int unk = 0; + + //int step = 0 + + for (int i = 0; i < size; i++) { + for (int j = 0; j < size; j++) { + var previousVersions = new HashSet<>(versions); + for (var version : previousVersions) { + + var model = store.createModelForState(version); + model.getInterpretation(friend).put(Tuple.of(i, j), true); + + Version version1 = model.commit(); + var stateCode = model.getAdapter(StateCoderAdapter.class).calculateStateCode(); + int code = stateCode.modelCode(); + //System.out.println(step+++" ->" +code); + if (codes.containsKey(code)) { + Version similar = codes.get(code).get(0); + + var outcome = storeAdapter.checkEquivalence(version1, similar); + if (outcome == StateEquivalenceChecker.EquivalenceResult.DIFFERENT) { + System.out.println(); + var c = model.getInterpretation(friend).getAll(); + while (c.move()) { + System.out.println(c.getKey().toString()); + } + System.out.println("vs"); + var c2 = store.createModelForState(similar).getInterpretation(friend).getAll(); + while (c2.move()) { + System.out.println(c2.getKey().toString()); + } + + dif++; + } else if (outcome == StateEquivalenceChecker.EquivalenceResult.UNKNOWN) { + unk++; + } else { + iso++; + } + } else { + versions.add(version1); + + List newList = new ArrayList<>(); + newList.add(version1); + codes.put(code, newList); + } + } + } + } + + System.out.printf("v=%d i=%d d=%d u=%d\n", versions.size(), iso, dif, unk); + } + + @Test + void runTests() { + for (int i = 0; i < 5; i++) { + System.out.println("size = " + i); + generate(i); + } + } +} diff --git a/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java b/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java new file mode 100644 index 00000000..b0b80af7 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java @@ -0,0 +1,80 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; + +import static org.junit.jupiter.api.Assertions.*; + +class StateCoderBuildTest { + Symbol person = new Symbol<>("Person", 1, Boolean.class, false); + Symbol age = new Symbol<>("age", 1, Integer.class, null); + Symbol friend = new Symbol<>("friend", 2, Boolean.class, false); + + @Test + void simpleStateCoderTest() { + var store = ModelStore.builder() + .symbols(person, age, friend) + .with(StateCoderAdapter + .builder()) + .build(); + + var model = store.createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + assertNotNull(stateCoder); + + var personI = model.getInterpretation(person); + var friendI = model.getInterpretation(friend); + var ageI = model.getInterpretation(age); + fill(personI, friendI, ageI); + + stateCoder.calculateStateCode(); + } + + @Test + void excludeTest() { + var store = ModelStore.builder() + .symbols(person, age, friend) + .with(StateCoderAdapter.builder() + .exclude(person) + .exclude(age)) + .build(); + + var model = store.createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + assertNotNull(stateCoder); + + var personI = model.getInterpretation(person); + var friendI = model.getInterpretation(friend); + var ageI = model.getInterpretation(age); + fill(personI, friendI, ageI); + + int code = stateCoder.calculateStateCode().modelCode(); + + ageI.put(Tuple.of(1),3); + assertEquals(code,stateCoder.calculateStateCode().modelCode()); + + ageI.put(Tuple.of(1),null); + assertEquals(code,stateCoder.calculateStateCode().modelCode()); + + personI.put(Tuple.of(2),false); + assertEquals(code,stateCoder.calculateStateCode().modelCode()); + } + + private static void fill(Interpretation personI, Interpretation friendI, Interpretation ageI) { + personI.put(Tuple.of(1), true); + personI.put(Tuple.of(2), true); + + ageI.put(Tuple.of(1), 5); + ageI.put(Tuple.of(2), 4); + + friendI.put(Tuple.of(1, 2), true); + } +} -- cgit v1.2.3-54-g00ecf From e1de155531c9e36a5ffa3e38f9b5764e7f8ab607 Mon Sep 17 00:00:00 2001 From: nagilooh Date: Fri, 4 Aug 2023 12:08:38 +0200 Subject: Add missing copyright headers --- .../src/main/java/tools/refinery/store/dse/ActionFactory.java | 5 +++++ .../tools/refinery/store/dse/DesignSpaceExplorationAdapter.java | 5 +++++ .../tools/refinery/store/dse/DesignSpaceExplorationBuilder.java | 5 +++++ .../refinery/store/dse/DesignSpaceExplorationStoreAdapter.java | 5 +++++ .../src/main/java/tools/refinery/store/dse/Strategy.java | 5 +++++ .../main/java/tools/refinery/store/dse/internal/Activation.java | 5 +++++ .../store/dse/internal/DesignSpaceExplorationAdapterImpl.java | 9 +++++++++ .../store/dse/internal/DesignSpaceExplorationBuilderImpl.java | 5 +++++ .../dse/internal/DesignSpaceExplorationStoreAdapterImpl.java | 5 +++++ .../tools/refinery/store/dse/internal/TransformationRule.java | 5 +++++ .../store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java | 1 + .../java/tools/refinery/store/dse/objectives/BaseObjective.java | 1 + .../java/tools/refinery/store/dse/objectives/Comparators.java | 1 + .../main/java/tools/refinery/store/dse/objectives/Fitness.java | 9 +++++++++ .../main/java/tools/refinery/store/dse/objectives/Objective.java | 1 + .../refinery/store/dse/objectives/ObjectiveComparatorHelper.java | 1 + .../tools/refinery/store/dse/strategy/BestFirstStrategy.java | 9 +++++++++ .../tools/refinery/store/dse/strategy/DepthFirstStrategy.java | 9 +++++++++ .../src/test/java/tools/refinery/store/dse/CRAExamplesTest.java | 5 +++++ .../src/test/java/tools/refinery/store/dse/DebugTest.java | 5 +++++ .../tools/refinery/store/dse/DesignSpaceExplorationTest.java | 5 +++++ .../java/tools/refinery/store/dse/TransformationRuleTest.java | 5 +++++ .../tools/refinery/visualization/ModelVisualizerAdapter.java | 5 +++++ .../tools/refinery/visualization/ModelVisualizerBuilder.java | 5 +++++ .../refinery/visualization/ModelVisualizerStoreAdapter.java | 5 +++++ .../java/tools/refinery/visualization/internal/FileFormat.java | 5 +++++ .../visualization/internal/ModelVisualizeStoreAdapterImpl.java | 5 +++++ .../visualization/internal/ModelVisualizerAdapterImpl.java | 9 +++++++-- .../visualization/internal/ModelVisualizerBuilderImpl.java | 5 +++++ 29 files changed, 143 insertions(+), 2 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java index 2af22963..524c2f55 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/ActionFactory.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse; import org.eclipse.collections.api.block.procedure.Procedure; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java index 729a6fc9..8963a496 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse; import tools.refinery.store.adapter.ModelAdapter; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java index 8ca0037d..4def5cb2 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse; import tools.refinery.store.adapter.ModelAdapterBuilder; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java index 5964cd82..186bfebb 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse; import tools.refinery.store.adapter.ModelStoreAdapter; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java index cef43386..e240f478 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse; public interface Strategy { diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java index f1de00e6..1893ce2e 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse.internal; import tools.refinery.store.tuple.Tuple; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java index 5fb54da9..b32e9696 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -1,3 +1,12 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * Copyright (c) 2023 The Refinery Authors + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ package tools.refinery.store.dse.internal; import tools.refinery.store.map.Version; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java index 03508adc..8f7056f2 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse.internal; import tools.refinery.store.adapter.AbstractModelAdapterBuilder; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java index b06462ce..09925ae7 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse.internal; import tools.refinery.store.adapter.ModelAdapter; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java index ed2e77f1..015d4815 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse.internal; import org.eclipse.collections.api.block.procedure.Procedure; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java index 82695704..afed75fd 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java index 24e3280d..7df33efe 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java index e64e04e8..476504b0 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java index 16caed85..92709d3e 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java @@ -1,3 +1,12 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * Copyright (c) 2023 The Refinery Authors + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ package tools.refinery.store.dse.objectives; import java.util.HashMap; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java index 4e14c9a3..c7313622 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java index 3184b8c4..1d676562 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java index 05cc5bac..8648864c 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java @@ -1,3 +1,12 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * Copyright (c) 2023 The Refinery Authors + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ package tools.refinery.store.dse.strategy; import tools.refinery.store.map.Version; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java index 42985013..1405789b 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java @@ -1,3 +1,12 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * Copyright (c) 2023 The Refinery Authors + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ package tools.refinery.store.dse.strategy; import tools.refinery.store.dse.DesignSpaceExplorationAdapter; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index e7cc60d6..4bdb05ec 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index 911c0661..101a5362 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java index 7db2c708..f4644407 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java index a32d392b..312bcebd 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java index b3f833ef..bc32323d 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.visualization; import tools.refinery.store.adapter.ModelAdapter; diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java index 9c1bd0e0..56cc425e 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.visualization; import tools.refinery.store.adapter.ModelAdapterBuilder; diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java index 764de6d4..6599d4c3 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.visualization; import tools.refinery.store.adapter.ModelStoreAdapter; diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java index 43d6eb3f..c5dffeb2 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/FileFormat.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.visualization.internal; public enum FileFormat { diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java index 6e158c28..b4db2682 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.visualization.internal; import tools.refinery.store.adapter.ModelAdapter; diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java index 8555da5f..06cc8113 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.visualization.internal; import tools.refinery.store.map.Version; @@ -201,7 +206,7 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return null; } - private Integer[] typePredicateColor(String name) { + private Integer[] typeColor(String name) { var random = new Random(name.hashCode()); return new Integer[] { random.nextInt(128) + 128, random.nextInt(128) + 128, random.nextInt(128) + 128 }; } @@ -212,7 +217,7 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { } // TODO: Only use interpretations where the value is not false (or unknown) var symbols = interpretations.stream() - .map(i -> typePredicateColor(i.getSymbol().name())).toArray(Integer[][]::new); + .map(i -> typeColor(i.getSymbol().name())).toArray(Integer[][]::new); diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java index 4148c24a..d19cf2cf 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ package tools.refinery.visualization.internal; import tools.refinery.store.adapter.AbstractModelAdapterBuilder; -- cgit v1.2.3-54-g00ecf From 814af4610f062c71a998aab2703d77cf07a84a3f Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Sat, 5 Aug 2023 03:07:25 +0200 Subject: Performance++: ModelVersion.equals is fast and simple and ModelVersion.hash cached. --- .../refinery/store/model/internal/ModelImpl.java | 8 ++------ .../store/model/internal/ModelVersion.java | 22 ++++++++++++---------- 2 files changed, 14 insertions(+), 16 deletions(-) (limited to 'subprojects') 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 92694af4..2b12d5a6 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 @@ -105,7 +105,7 @@ public class ModelImpl implements Model { // Doing the commit on the interpretations Version[] interpretationVersions = new Version[interpretations.size()]; int j = 0; - for(var interpretationEntry : interpretations.entrySet()) { + for (var interpretationEntry : interpretations.entrySet()) { interpretationVersions[j++] = interpretationEntry.getValue().commit(); } ModelVersion modelVersion = new ModelVersion(interpretationVersions); @@ -140,7 +140,7 @@ public class ModelImpl implements Model { } int j = 0; for (var interpretation : interpretations.values()) { - interpretation.restore(ModelVersion.getInternalVersion(version,j++)); + interpretation.restore(ModelVersion.getInternalVersion(version, j++)); } setState(version); @@ -187,8 +187,4 @@ public class ModelImpl implements Model { public void removeListener(ModelListener listener) { listeners.remove(listener); } - - public Map> getInterpretations() { - return interpretations; - } } 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 cf3b7fc6..f81386f1 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 @@ -9,25 +9,27 @@ import tools.refinery.store.map.Version; import java.util.Arrays; -public record ModelVersion(Version[] mapVersions) implements Version{ +public class ModelVersion implements Version { + final Version[] mapVersions; + final int hash; + + public ModelVersion(Version[] mapVersions) { + this.mapVersions = mapVersions; + this.hash = Arrays.hashCode(mapVersions); + } public static Version getInternalVersion(Version modelVersion, int interpretationIndex) { - return ((ModelVersion)modelVersion).mapVersions()[interpretationIndex]; + return ((ModelVersion) modelVersion).mapVersions[interpretationIndex]; } @Override public int hashCode() { - return Arrays.hashCode(mapVersions); + return hash; } @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - ModelVersion that = (ModelVersion) o; - - return Arrays.equals(mapVersions, that.mapVersions); + public boolean equals(Object obj) { + return super.equals(obj); } @Override -- cgit v1.2.3-54-g00ecf From 8eb1b7a14972e52018a1bb69a53f2878e96b581e Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Mon, 7 Aug 2023 02:45:57 +0200 Subject: StateCoderBuilder improvement with individuals, and customizable calculators. --- .../statecoding/StateCodeCalculatorFactory.java | 15 +++ .../store/statecoding/StateCoderBuilder.java | 16 +++ .../store/statecoding/StateEquivalenceChecker.java | 2 + .../internal/StateCoderAdapterImpl.java | 12 +- .../internal/StateCoderBuilderImpl.java | 34 +++++- .../internal/StateCoderStoreAdapterImpl.java | 29 +++-- .../AbstractNeighbourhoodCalculator.java | 95 +++++++++++++++ .../neighbourhood/LazyNeighbourhoodCalculator.java | 100 ++++------------ .../LazyNeighbourhoodCalculatorFactory.java | 20 ++++ .../neighbourhood/NeighbourhoodCalculator.java | 132 --------------------- .../StateEquivalenceCheckerImpl.java | 42 ++++--- .../store/statecoding/StateCoderBuildTest.java | 51 +++++++- 12 files changed, 297 insertions(+), 251 deletions(-) create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculatorFactory.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java (limited to 'subprojects') 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 new file mode 100644 index 00000000..04e17a13 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCodeCalculatorFactory.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +import org.eclipse.collections.api.set.primitive.IntSet; +import tools.refinery.store.model.Interpretation; + +import java.util.List; + +public interface StateCodeCalculatorFactory { + StateCodeCalculator create(List> interpretations, IntSet individuals); +} diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderBuilder.java index 2f37584f..54650825 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/StateCoderBuilder.java @@ -8,7 +8,9 @@ package tools.refinery.store.statecoding; import tools.refinery.store.adapter.ModelAdapterBuilder; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.AnySymbol; +import tools.refinery.store.tuple.Tuple1; +import java.util.Arrays; import java.util.Collection; import java.util.List; @@ -24,6 +26,20 @@ public interface StateCoderBuilder extends ModelAdapterBuilder { return excludeAll(List.of(symbols)); } + StateCoderBuilder individual(Tuple1 tuple); + default StateCoderBuilder individual(Collection tuple1s) { + for(Tuple1 tuple : tuple1s){ + individual(tuple); + } + return this; + } + default StateCoderBuilder individuals(Tuple1... tuple1s) { + return individual(Arrays.stream(tuple1s).toList()); + } + + StateCoderBuilder stateCodeCalculatorFactory(StateCodeCalculatorFactory codeCalculatorFactory); + StateCoderBuilder stateEquivalenceChecker(StateEquivalenceChecker stateEquivalenceChecker); + @Override StateCoderStoreAdapter build(ModelStore store); } 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 6d8dc6c7..3fd8c8d8 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 @@ -5,6 +5,7 @@ */ package tools.refinery.store.statecoding; +import org.eclipse.collections.api.set.primitive.IntSet; import tools.refinery.store.model.Interpretation; import java.util.List; @@ -15,6 +16,7 @@ public interface StateEquivalenceChecker { } EquivalenceResult constructMorphism( + IntSet individuals, List> interpretations1, ObjectCode code1, List> interpretations2, diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java index b66fc86d..a2471916 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/internal/StateCoderAdapterImpl.java @@ -6,28 +6,20 @@ package tools.refinery.store.statecoding.internal; import tools.refinery.store.adapter.ModelStoreAdapter; -import tools.refinery.store.model.Interpretation; import tools.refinery.store.model.Model; -import tools.refinery.store.representation.Symbol; import tools.refinery.store.statecoding.StateCodeCalculator; import tools.refinery.store.statecoding.StateCoderAdapter; import tools.refinery.store.statecoding.StateCoderResult; -import tools.refinery.store.statecoding.neighbourhood.LazyNeighbourhoodCalculator; - -import java.util.Collection; -import java.util.List; public class StateCoderAdapterImpl implements StateCoderAdapter { final ModelStoreAdapter storeAdapter; final Model model; final StateCodeCalculator calculator; - StateCoderAdapterImpl(ModelStoreAdapter storeAdapter, Model model, Collection> symbols) { + StateCoderAdapterImpl(ModelStoreAdapter storeAdapter, StateCodeCalculator calculator, Model model) { this.storeAdapter = storeAdapter; this.model = model; - - List> interpretations = symbols.stream().map(model::getInterpretation).toList(); - calculator = new LazyNeighbourhoodCalculator(interpretations); + this.calculator = calculator; } @Override 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 700723f4..8268a826 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,19 +5,27 @@ */ package tools.refinery.store.statecoding.internal; +import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.StateCodeCalculatorFactory; import tools.refinery.store.statecoding.StateCoderBuilder; import tools.refinery.store.statecoding.StateCoderStoreAdapter; +import tools.refinery.store.statecoding.StateEquivalenceChecker; +import tools.refinery.store.statecoding.neighbourhood.LazyNeighbourhoodCalculatorFactory; +import tools.refinery.store.statecoding.stateequivalence.StateEquivalenceCheckerImpl; +import tools.refinery.store.tuple.Tuple1; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.*; public class StateCoderBuilderImpl implements StateCoderBuilder { Set excluded = new HashSet<>(); + IntHashSet individuals = new IntHashSet(); + + StateCodeCalculatorFactory calculator = new LazyNeighbourhoodCalculatorFactory(); + StateEquivalenceChecker checker = new StateEquivalenceCheckerImpl(); @Override public StateCoderBuilder exclude(AnySymbol symbol) { @@ -25,6 +33,24 @@ public class StateCoderBuilderImpl implements StateCoderBuilder { return this; } + @Override + public StateCoderBuilder individual(Tuple1 tuple) { + individuals.add(tuple.get(0)); + return this; + } + + @Override + public StateCoderBuilder stateEquivalenceChecker(StateEquivalenceChecker stateEquivalenceChecker) { + this.checker = stateEquivalenceChecker; + return this; + } + + @Override + public StateCoderBuilder stateCodeCalculatorFactory(StateCodeCalculatorFactory codeCalculatorFactory) { + this.calculator = codeCalculatorFactory; + return this; + } + @Override public boolean isConfigured() { return true; @@ -43,6 +69,6 @@ public class StateCoderBuilderImpl implements StateCoderBuilder { symbols.add(typed); } } - return new StateCoderStoreAdapterImpl(store, symbols); + return new StateCoderStoreAdapterImpl(store, calculator, checker, symbols, individuals); } } 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 5374755d..89586bfb 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 @@ -5,14 +5,15 @@ */ package tools.refinery.store.statecoding.internal; +import org.eclipse.collections.api.set.primitive.IntSet; import tools.refinery.store.map.Version; import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.StateCodeCalculatorFactory; import tools.refinery.store.statecoding.StateCoderAdapter; import tools.refinery.store.statecoding.StateCoderStoreAdapter; import tools.refinery.store.statecoding.StateEquivalenceChecker; -import tools.refinery.store.statecoding.stateequivalence.StateEquivalenceCheckerImpl; import java.util.Collection; import java.util.Objects; @@ -20,12 +21,22 @@ import java.util.Objects; public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { final ModelStore store; final Collection> symbols; + final IntSet individuals; - final StateEquivalenceChecker equivalenceChecker = new StateEquivalenceCheckerImpl(); + final StateEquivalenceChecker equivalenceChecker; + final StateCodeCalculatorFactory codeCalculatorFactory; - StateCoderStoreAdapterImpl(ModelStore store, Collection> symbols) { + StateCoderStoreAdapterImpl(ModelStore store, + StateCodeCalculatorFactory codeCalculatorFactory, + StateEquivalenceChecker equivalenceChecker, + Collection> symbols, + IntSet individuals) + { + this.codeCalculatorFactory = codeCalculatorFactory; + this.equivalenceChecker = equivalenceChecker; this.store = store; this.symbols = symbols; + this.individuals = individuals; } @Override @@ -35,7 +46,7 @@ public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { @Override public StateEquivalenceChecker.EquivalenceResult checkEquivalence(Version v1, Version v2) { - if(Objects.equals(v1,v2)) { + if (Objects.equals(v1, v2)) { return StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC; } var model1 = this.getStore().createModelForState(v1); @@ -44,20 +55,20 @@ public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { var s1 = model1.getAdapter(StateCoderAdapter.class).calculateStateCode(); var s2 = model2.getAdapter(StateCoderAdapter.class).calculateStateCode(); - if(s1.modelCode() != s2.modelCode()) { + if (s1.modelCode() != s2.modelCode()) { return StateEquivalenceChecker.EquivalenceResult.DIFFERENT; } var i1 = symbols.stream().map(model1::getInterpretation).toList(); var i2 = symbols.stream().map(model2::getInterpretation).toList(); - return equivalenceChecker.constructMorphism(i1,s1.objectCode(),i2,s2.objectCode()); + return equivalenceChecker.constructMorphism(individuals, i1, s1.objectCode(), i2, s2.objectCode()); } @Override public StateCoderAdapter createModelAdapter(Model model) { - return new StateCoderAdapterImpl(this,model,symbols); + var interpretations = symbols.stream().map(model::getInterpretation).toList(); + var coder = codeCalculatorFactory.create(interpretations, individuals); + return new StateCoderAdapterImpl(this, coder, model); } - - } 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 new file mode 100644 index 00000000..0de76519 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.neighbourhood; + +import org.eclipse.collections.api.set.primitive.IntSet; +import org.eclipse.collections.impl.map.mutable.primitive.IntLongHashMap; +import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.statecoding.ObjectCode; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple0; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Random; + +public abstract class AbstractNeighbourhoodCalculator { + protected final List> nullImpactValues; + protected final LinkedHashMap, long[]> impactValues; + protected final IntLongHashMap individualHashValues; + + protected AbstractNeighbourhoodCalculator(List> interpretations, IntSet individuals) { + this.nullImpactValues = new ArrayList<>(); + this.impactValues = new LinkedHashMap<>(); + Random random = new Random(1); + + individualHashValues = new IntLongHashMap(); + var individualsInOrder = individuals.toSortedList(Integer::compare); + for(int i = 0; i interpretation : interpretations) { + int arity = interpretation.getSymbol().arity(); + if (arity == 0) { + nullImpactValues.add(interpretation); + } else { + long[] impact = new long[arity]; + for (int i = 0; i < arity; i++) { + impact[i] = random.nextInt(); + } + impactValues.put(interpretation, impact); + } + } + } + + protected void initializeWithIndividuals(ObjectCodeImpl previous, LongIntHashMap hash2Amount) { + for (var entry : individualHashValues.keyValuesView()) { + previous.set(entry.getOne(), entry.getTwo()); + hash2Amount.put(entry.getTwo(), 1); + } + } + + protected long getTupleHash1(Tuple tuple, Object value, ObjectCode objectCodeImpl) { + long result = value.hashCode(); + result = result * 31 + objectCodeImpl.get(tuple.get(0)); + return result; + } + + protected long getTupleHash2(Tuple tuple, Object value, ObjectCode objectCodeImpl) { + long result = value.hashCode(); + result = result * 31 + objectCodeImpl.get(tuple.get(0)); + result = result * 31 + objectCodeImpl.get(tuple.get(1)); + if (tuple.get(0) == tuple.get(1)) { + result *= 31; + } + return result; + } + + protected long getTupleHashN(Tuple tuple, Object value, ObjectCode objectCodeImpl) { + long result = value.hashCode(); + for (int i = 0; i < tuple.getSize(); i++) { + result = result * 31 + objectCodeImpl.get(tuple.get(i)); + } + return result; + } + + protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { + long x = tupleHash * impact; + objectCodeImpl.set(o, objectCodeImpl.get(o) + x); + } + + protected long calculateModelCode(long lastSum) { + long result = 1; + for (var nullImpactValue : nullImpactValues) { + result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); + } + result += lastSum; + 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 79317679..98a75e08 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 @@ -6,74 +6,48 @@ package tools.refinery.store.statecoding.neighbourhood; import org.eclipse.collections.api.map.primitive.LongIntMap; +import org.eclipse.collections.api.set.primitive.IntSet; import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; import tools.refinery.store.map.Cursor; import tools.refinery.store.model.Interpretation; import tools.refinery.store.statecoding.StateCodeCalculator; import tools.refinery.store.statecoding.StateCoderResult; import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.Tuple0; -import java.util.ArrayList; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Random; -public class LazyNeighbourhoodCalculator implements StateCodeCalculator { - protected final List> nullImpactValues; - protected final LinkedHashMap, long[]> impactValues; - - public LazyNeighbourhoodCalculator(List> interpretations) { - this.nullImpactValues = new ArrayList<>(); - this.impactValues = new LinkedHashMap<>(); - Random random = new Random(1); - - for (Interpretation interpretation : interpretations) { - int arity = interpretation.getSymbol().arity(); - if (arity == 0) { - nullImpactValues.add(interpretation); - } else { - long[] impact = new long[arity]; - for (int i = 0; i < arity; i++) { - impact[i] = random.nextInt(); - } - impactValues.put(interpretation, impact); - } - } +public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { + public LazyNeighbourhoodCalculator(List> interpretations, IntSet individuals) { + super(interpretations, individuals); } public StateCoderResult calculateCodes() { ObjectCodeImpl previous = new ObjectCodeImpl(); - ObjectCodeImpl next = new ObjectCodeImpl(); - LongIntMap hash2Amount = new LongIntHashMap(); + LongIntHashMap hash2Amount = new LongIntHashMap(); + + initializeWithIndividuals(previous, hash2Amount); long lastSum; - int lastSize = 1; - boolean grows; + // All hash code is 0, except to the individuals. + int lastSize = hash2Amount.size() + 1; + boolean grows; do { + ObjectCodeImpl next = new ObjectCodeImpl(); constructNextObjectCodes(previous, next, hash2Amount); LongIntHashMap nextHash2Amount = new LongIntHashMap(); lastSum = calculateLastSum(previous, next, hash2Amount, nextHash2Amount); - previous = next; - next = null; - int nextSize = nextHash2Amount.size(); grows = nextSize > lastSize; lastSize = nextSize; - if (grows) { - next = new ObjectCodeImpl(previous); - } + previous = next; + hash2Amount = nextHash2Amount; } while (grows); - long result = 1; - for (var nullImpactValue : nullImpactValues) { - result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); - } - result += lastSum; + long result = calculateModelCode(lastSum); return new StateCoderResult((int) result, previous); } @@ -96,7 +70,7 @@ public class LazyNeighbourhoodCalculator implements StateCodeCalculator { final long shifted1 = hash >>> 8; final long shifted2 = hash << 8; final long shifted3 = hash >> 2; - lastSum += shifted1*shifted3 + shifted2; + lastSum += shifted1 * shifted3 + shifted2; } return lastSum; } @@ -126,7 +100,7 @@ public class LazyNeighbourhoodCalculator implements StateCodeCalculator { private boolean isUnique(LongIntMap hash2Amount, ObjectCodeImpl objectCodeImpl, int object) { final long hash = objectCodeImpl.get(object); - if(hash == 0) { + if (hash == 0) { return false; } final int amount = hash2Amount.get(hash); @@ -149,12 +123,12 @@ public class LazyNeighbourhoodCalculator implements StateCodeCalculator { } private void lazyImpactCalculation2(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor cursor) { - Tuple tuple = cursor.getKey(); - int o1 = tuple.get(0); - int o2 = tuple.get(1); + final Tuple tuple = cursor.getKey(); + final int o1 = tuple.get(0); + final int o2 = tuple.get(1); - boolean u1 = isUnique(hash2Amount, previous, o1); - boolean u2 = isUnique(hash2Amount, previous, o2); + final boolean u1 = isUnique(hash2Amount, previous, o1); + final boolean u2 = isUnique(hash2Amount, previous, o2); if (u1 && u2) { next.ensureSize(o1); @@ -175,9 +149,9 @@ public class LazyNeighbourhoodCalculator implements StateCodeCalculator { } private void lazyImpactCalculationN(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor cursor) { - Tuple tuple = cursor.getKey(); + final Tuple tuple = cursor.getKey(); - boolean[] uniques = new boolean[tuple.getSize()]; + final boolean[] uniques = new boolean[tuple.getSize()]; boolean allUnique = true; for (int i = 0; i < tuple.getSize(); i++) { final boolean isUnique = isUnique(hash2Amount, previous, tuple.get(i)); @@ -204,32 +178,4 @@ public class LazyNeighbourhoodCalculator implements StateCodeCalculator { } } - private long getTupleHash1(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { - long result = value.hashCode(); - result = result * 31 + objectCodeImpl.get(tuple.get(0)); - return result; - } - - private long getTupleHash2(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { - long result = value.hashCode(); - result = result * 31 + objectCodeImpl.get(tuple.get(0)); - result = result * 31 + objectCodeImpl.get(tuple.get(1)); - if (tuple.get(0) == tuple.get(1)) { - result*=31; - } - return result; - } - - private long getTupleHashN(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { - long result = value.hashCode(); - for (int i = 0; i < tuple.getSize(); i++) { - result = result * 31 + objectCodeImpl.get(tuple.get(i)); - } - return result; - } - - protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { - long x = tupleHash * impact; - objectCodeImpl.set(o, objectCodeImpl.get(o) + x); - } } diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculatorFactory.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculatorFactory.java new file mode 100644 index 00000000..2e499f95 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculatorFactory.java @@ -0,0 +1,20 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.neighbourhood; + +import org.eclipse.collections.api.set.primitive.IntSet; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.statecoding.StateCodeCalculator; +import tools.refinery.store.statecoding.StateCodeCalculatorFactory; + +import java.util.List; + +public class LazyNeighbourhoodCalculatorFactory implements StateCodeCalculatorFactory { + @Override + public StateCodeCalculator create(List> interpretations, IntSet individuals) { + return new LazyNeighbourhoodCalculator(interpretations,individuals); + } +} 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 deleted file mode 100644 index 212291c3..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.statecoding.neighbourhood; - -import org.eclipse.collections.api.set.primitive.MutableLongSet; -import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; -import tools.refinery.store.model.Interpretation; -import tools.refinery.store.statecoding.StateCodeCalculator; -import tools.refinery.store.statecoding.StateCoderResult; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.Tuple0; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Random; - -public class NeighbourhoodCalculator implements StateCodeCalculator { - protected final List> nullImpactValues; - protected final LinkedHashMap, long[]> impactValues; - - public NeighbourhoodCalculator(List> interpretations) { - this.nullImpactValues = new ArrayList<>(); - this.impactValues = new LinkedHashMap<>(); - Random random = new Random(1); - - for (Interpretation interpretation : interpretations) { - int arity = interpretation.getSymbol().arity(); - if (arity == 0) { - nullImpactValues.add(interpretation); - } else { - long[] impact = new long[arity]; - for (int i = 0; i < arity; i++) { - impact[i] = random.nextLong(); - } - impactValues.put(interpretation, impact); - } - } - } - - @Override - public StateCoderResult calculateCodes() { - ObjectCodeImpl previous = new ObjectCodeImpl(); - ObjectCodeImpl next = new ObjectCodeImpl(); - - int previousSize = 1; - long lastSum; - boolean grows; - - do{ - for (var impactValueEntry : this.impactValues.entrySet()) { - Interpretation interpretation = impactValueEntry.getKey(); - long[] impact = impactValueEntry.getValue(); - var cursor = interpretation.getAll(); - while (cursor.move()) { - Tuple tuple = cursor.getKey(); - Object value = cursor.getValue(); - long tupleHash = getTupleHash(tuple, value, previous); - addHash(next, tuple, impact, tupleHash); - } - } - - previous = next; - next = null; - lastSum = 0; - MutableLongSet codes = new LongHashSet(); - for (int i = 0; i < previous.getSize(); i++) { - long objectHash = previous.get(i); - codes.add(objectHash); - - final long shifted1 = objectHash>>> 32; - final long shifted2 = objectHash << 32; - lastSum += shifted1 + shifted2; - } - int nextSize = codes.size(); - grows = previousSize < nextSize; - previousSize = nextSize; - - if(grows) { - next = new ObjectCodeImpl(previous); - } - } while (grows); - - long result = 1; - for (var nullImpactValue : nullImpactValues) { - result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); - } - result += lastSum; - - return new StateCoderResult((int) result, previous); - } - - protected long getTupleHash(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { - long result = value.hashCode(); - int arity = tuple.getSize(); - if (arity == 1) { - result = result * 31 + objectCodeImpl.get(tuple.get(0)); - } else if (arity == 2) { - result = result * 31 + objectCodeImpl.get(tuple.get(0)); - result = result * 31 + objectCodeImpl.get(tuple.get(1)); - if (tuple.get(0) == tuple.get(1)) { - result++; - } - } else if (arity > 2) { - for (int i = 0; i < arity; i++) { - result = result * 31 + objectCodeImpl.get(tuple.get(i)); - } - } - return result; - } - - protected void addHash(ObjectCodeImpl objectCodeImpl, Tuple tuple, long[] impact, long tupleHashCode) { - if (tuple.getSize() == 1) { - addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); - } else if (tuple.getSize() == 2) { - addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); - addHash(objectCodeImpl, tuple.get(1), impact[1], tupleHashCode); - } else if (tuple.getSize() > 2) { - for (int i = 0; i < tuple.getSize(); i++) { - addHash(objectCodeImpl, tuple.get(i), impact[i], tupleHashCode); - } - } - } - - protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { - objectCodeImpl.set(o, objectCodeImpl.get(o) + tupleHash * impact); - } - -} 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 fd704086..e58a2502 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 @@ -5,7 +5,9 @@ */ package tools.refinery.store.statecoding.stateequivalence; +import org.eclipse.collections.api.factory.primitive.IntIntMaps; import org.eclipse.collections.api.map.primitive.IntIntMap; +import org.eclipse.collections.api.set.primitive.IntSet; import org.eclipse.collections.impl.map.mutable.primitive.IntIntHashMap; import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; @@ -23,7 +25,8 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { public static final int LIMIT = 1000; @Override - public EquivalenceResult constructMorphism(List> interpretations1, + public EquivalenceResult constructMorphism(IntSet individuals, + List> interpretations1, ObjectCode code1, List> interpretations2, ObjectCode code2) { @@ -34,7 +37,8 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { IntIntHashMap object2PermutationGroup = new IntIntHashMap(); List> permutationsGroups = new ArrayList<>(); - final EquivalenceResult permutations = constructPermutationNavigation(indexByHash(code1), indexByHash(code2), + final EquivalenceResult permutations = constructPermutationNavigation(individuals, + indexByHash(code1, individuals), indexByHash(code2, individuals), object2PermutationGroup, permutationsGroups); if (permutations == EquivalenceResult.DIFFERENT) { @@ -60,24 +64,27 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { return EquivalenceResult.DIFFERENT; } - private LongObjectHashMap indexByHash(ObjectCode code) { + private LongObjectHashMap indexByHash(ObjectCode code, IntSet individuals) { LongObjectHashMap result = new LongObjectHashMap<>(); for (int o = 0; o < code.getSize(); o++) { - long hash = code.get(o); - var equivalenceClass = result.get(hash); - if (equivalenceClass == null) { - equivalenceClass = new IntHashSet(); - result.put(hash, equivalenceClass); + if(! individuals.contains(o)){ + long hash = code.get(o); + var equivalenceClass = result.get(hash); + if (equivalenceClass == null) { + equivalenceClass = new IntHashSet(); + result.put(hash, equivalenceClass); + } + equivalenceClass.add(o); } - equivalenceClass.add(o); } return result; } - private EquivalenceResult constructPermutationNavigation(LongObjectHashMap map1, - LongObjectHashMap map2, - IntIntHashMap emptyMapToListOfOptions, - List> emptyListOfOptions) { + private EquivalenceResult constructPermutationNavigation(IntSet individuals, + LongObjectHashMap map1, + LongObjectHashMap map2, + IntIntHashMap object2OptionIndex, + List> listOfOptions) { if (map1.size() != map2.size()) { return EquivalenceResult.DIFFERENT; } @@ -101,10 +108,13 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { allComplete &= pairing.isComplete(); - final int optionIndex = emptyListOfOptions.size(); - set1.forEach(key -> emptyMapToListOfOptions.put(key, optionIndex)); - emptyListOfOptions.add(pairing.permutations()); + final int optionIndex = listOfOptions.size(); + set1.forEach(key -> object2OptionIndex.put(key, optionIndex)); + listOfOptions.add(pairing.permutations()); } + + individuals.forEach(o -> listOfOptions.add(o,List.of(IntIntMaps.immutable.of(o,o)))); + if(allComplete) { return EquivalenceResult.ISOMORPHIC; } else { diff --git a/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java b/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java index b0b80af7..a4e953ea 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java @@ -19,11 +19,10 @@ class StateCoderBuildTest { Symbol friend = new Symbol<>("friend", 2, Boolean.class, false); @Test - void simpleStateCoderTest() { + void simpleStateCoderBuildTest() { var store = ModelStore.builder() .symbols(person, age, friend) - .with(StateCoderAdapter - .builder()) + .with(StateCoderAdapter.builder()) .build(); var model = store.createEmptyModel(); @@ -33,6 +32,7 @@ class StateCoderBuildTest { var personI = model.getInterpretation(person); var friendI = model.getInterpretation(friend); var ageI = model.getInterpretation(age); + fill(personI, friendI, ageI); stateCoder.calculateStateCode(); @@ -68,6 +68,51 @@ class StateCoderBuildTest { assertEquals(code,stateCoder.calculateStateCode().modelCode()); } + @Test + void notIndividualTest() { + var store = ModelStore.builder() + .symbols(friend) + .with(StateCoderAdapter.builder()) + .build(); + + var model = store.createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + + var friendI = model.getInterpretation(friend); + + friendI.put(Tuple.of(1,2),true); + int code1 = stateCoder.calculateModelCode(); + + friendI.put(Tuple.of(1,2),false); + friendI.put(Tuple.of(2,1),true); + int code2 = stateCoder.calculateModelCode(); + + assertEquals(code1,code2); + } + + @Test + void individualTest() { + var store = ModelStore.builder() + .symbols(friend) + .with(StateCoderAdapter.builder() + .individual(Tuple.of(1))) + .build(); + + var model = store.createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + + var friendI = model.getInterpretation(friend); + + friendI.put(Tuple.of(1,2),true); + int code1 = stateCoder.calculateModelCode(); + + friendI.put(Tuple.of(1,2),false); + friendI.put(Tuple.of(2,1),true); + int code2 = stateCoder.calculateModelCode(); + + assertNotEquals(code1,code2); + } + private static void fill(Interpretation personI, Interpretation friendI, Interpretation ageI) { personI.put(Tuple.of(1), true); personI.put(Tuple.of(2), true); -- cgit v1.2.3-54-g00ecf From e722deec08fe509f3e5a2b701c7e3260ee10aa66 Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Mon, 7 Aug 2023 12:04:58 +0200 Subject: Update exploration strategies - Fix best first search algorithm - Improve efficiency of exploration adapter --- .../store/dse/DesignSpaceExplorationAdapter.java | 6 +- .../dse/DesignSpaceExplorationStoreAdapter.java | 18 +++++ .../java/tools/refinery/store/dse/Strategy.java | 4 +- .../DesignSpaceExplorationAdapterImpl.java | 92 ++++++++++++---------- .../DesignSpaceExplorationStoreAdapterImpl.java | 27 ++++--- .../store/dse/internal/TransformationRule.java | 16 +++- .../store/dse/strategy/BestFirstStrategy.java | 36 +++++++-- .../store/dse/strategy/DepthFirstStrategy.java | 20 ++++- .../refinery/store/dse/TransformationRuleTest.java | 18 ++--- 9 files changed, 161 insertions(+), 76 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java index 8963a496..c4aa97c2 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java @@ -38,7 +38,9 @@ public interface DesignSpaceExplorationAdapter extends ModelAdapter { public boolean backtrack(); - public Fitness calculateFitness(); + public boolean backtrack(String reason); + + public Fitness getFitness(); public void newSolution(); @@ -63,4 +65,6 @@ public interface DesignSpaceExplorationAdapter extends ModelAdapter { public void setRandom(Random random); public void setRandom(long seed); + + public List getSolutions(); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java index 186bfebb..0252748d 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java @@ -6,6 +6,24 @@ package tools.refinery.store.dse; import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.objectives.Objective; +import tools.refinery.store.model.Model; +import tools.refinery.store.query.dnf.RelationalQuery; + +import java.util.List; +import java.util.Set; public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter { + + @Override + DesignSpaceExplorationAdapter createModelAdapter(Model model); + + Set getTransformationSpecifications(); + + Set getGlobalConstraints(); + + List getObjectives(); + + Strategy getStrategy(); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java index e240f478..409fe8a6 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java @@ -7,7 +7,7 @@ package tools.refinery.store.dse; public interface Strategy { - public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter); + void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter); - public void explore(); + void explore(); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java index b32e9696..4e5cc467 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -33,26 +33,28 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration private final Model model; private final ModelQueryAdapter queryEngine; private final DesignSpaceExplorationStoreAdapterImpl storeAdapter; - private final LinkedHashSet transformationRules; - private final LinkedHashSet globalConstraints; + private final Set transformationRules; + private final Set globalConstraints; private final List objectives; private final LinkedHashSet> globalConstraintResultSets = new LinkedHashSet<>(); private final Interpretation sizeInterpretation; private final Strategy strategy; private ObjectiveComparatorHelper objectiveComparatorHelper; - private List trajectory = new LinkedList<>(); + private List trajectory = new ArrayList<>(); + private Map parents = new HashMap<>(); private Fitness lastFitness; - private final LinkedHashSet solutions = new LinkedHashSet<>(); - private Map> statesAndUntraversedActivations; - private Map> statesAndTraversedActivations; + private final List solutions = new ArrayList<>(); + private Map> statesAndTraversedActivations; private Random random = new Random(); private boolean isNewState = false; private final boolean isVisualizationEnabled; private final ModelVisualizerAdapter modelVisualizerAdapter; + private final Map fitnessCache = new HashMap<>(); + public List getTrajectory() { - return new LinkedList<>(trajectory); + return new ArrayList<>(trajectory); } public DesignSpaceExplorationAdapterImpl(Model model, DesignSpaceExplorationStoreAdapterImpl storeAdapter) { @@ -72,7 +74,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } objectives = storeAdapter.getObjectives(); - statesAndUntraversedActivations = new HashMap<>(); statesAndTraversedActivations = new HashMap<>(); strategy = storeAdapter.getStrategy(); modelVisualizerAdapter = model.tryGetAdapter(ModelVisualizerAdapter.class).orElse(null); @@ -91,11 +92,9 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } @Override - public LinkedHashSet explore() { + public List explore() { var state = model.commit(); trajectory.add(state); - statesAndUntraversedActivations.put(state, getAllActivations()); - statesAndTraversedActivations.put(state, new LinkedHashSet<>()); strategy.initStrategy(this); strategy.explore(); return solutions; @@ -137,14 +136,22 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration @Override public boolean backtrack() { + return backtrack(""); + } + @Override + public boolean backtrack(String reason) { if (trajectory.size() < 2) { return false; } + var currentState = model.getState(); + if (!parents.containsKey(currentState)) { + return false; + } if (isVisualizationEnabled) { modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 1), - trajectory.get(trajectory.size() - 2), "backtrack"); + trajectory.get(trajectory.size() - 2), "backtrack(" + reason + ")"); } - model.restore(trajectory.get(trajectory.size() - 2)); + model.restore(parents.get(model.getState())); trajectory.remove(trajectory.size() - 1); return true; } @@ -156,7 +163,7 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration // modelVisualizerAdapter.addTransition(this.trajectory.get(trajectory.size() - 1), // trajectory.get(trajectory.size() - 1), "restore"); // } - this.trajectory = trajectory; + this.trajectory = new ArrayList<>(trajectory); } @@ -171,7 +178,18 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } @Override - public Fitness calculateFitness() { + public List getSolutions() { + return solutions; + } + + @Override + public Fitness getFitness() { + var result = fitnessCache.computeIfAbsent(model.getState(), s -> calculateFitness()); + lastFitness = result; + return result; + } + + private Fitness calculateFitness() { Fitness result = new Fitness(); boolean satisfiesHardObjectives = true; for (Objective objective : objectives) { @@ -203,15 +221,19 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } public LinkedHashSet getUntraversedActivations() { -// return statesAndUntraversedActivations.get(model.getState()); - LinkedHashSet untraversedActivations = new LinkedHashSet<>(); - for (Activation activation : getAllActivations()) { - if (!statesAndTraversedActivations.get(model.getState()).contains(activation)) { - untraversedActivations.add(activation); + var traversedActivations = statesAndTraversedActivations.get(model.getState()); + if (traversedActivations == null) { + return new LinkedHashSet<>(getAllActivations()); + } + else { + LinkedHashSet untraversedActivations = new LinkedHashSet<>(); + for (Activation activation : getAllActivations()) { + if (!traversedActivations.contains(activation)) { + untraversedActivations.add(activation); + } } + return untraversedActivations; } - - return untraversedActivations; } @Override @@ -220,26 +242,20 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration return false; } var previousState = model.getState(); - if (!statesAndUntraversedActivations.get(previousState).contains(activation)) { -// TODO: throw exception? - return false; - } if (!activation.fire()) { return false; } - statesAndUntraversedActivations.get(previousState).remove(activation); - statesAndTraversedActivations.get(previousState).add(activation); + statesAndTraversedActivations.computeIfAbsent(previousState, s -> new ArrayList<>()).add(activation); var newState = model.commit(); trajectory.add(newState); - isNewState = !statesAndUntraversedActivations.containsKey(newState); - statesAndUntraversedActivations.put(newState, getAllActivations()); - statesAndTraversedActivations.put(newState, new LinkedHashSet<>()); + parents.put(newState, previousState); + isNewState = !statesAndTraversedActivations.containsKey(newState); if (isVisualizationEnabled) { if (isNewState) { modelVisualizerAdapter.addState(newState); } - modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 2), - trajectory.get(trajectory.size() - 1), activation.transformationRule().getName(), + // TODO: Change to this: + modelVisualizerAdapter.addTransition(previousState, newState, activation.transformationRule().getName(), activation.activation()); } return true; @@ -266,10 +282,10 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration return trajectory.contains(model.getState()); } - public LinkedHashSet getAllActivations() { - LinkedHashSet result = new LinkedHashSet<>(); + public List getAllActivations() { + List result = new LinkedList<>(); for (var rule : transformationRules) { - result.addAll(rule.getAllActivations()); + result.addAll(rule.getAllActivationsAsList()); } return result; } @@ -279,10 +295,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration return !isNewState; } - public Fitness getLastFitness() { - return lastFitness; - } - public ObjectiveComparatorHelper getObjectiveComparatorHelper() { if (objectiveComparatorHelper == null) { objectiveComparatorHelper = new ObjectiveComparatorHelper(objectives); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java index 09925ae7..fea39886 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java @@ -5,27 +5,26 @@ */ package tools.refinery.store.dse.internal; -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.dnf.RelationalQuery; import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter; import tools.refinery.store.dse.Strategy; import tools.refinery.store.dse.objectives.Objective; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.dnf.RelationalQuery; -import java.util.LinkedHashSet; import java.util.List; +import java.util.Set; public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { private final ModelStore store; - private final LinkedHashSet transformationSpecifications; - private final LinkedHashSet globalConstraints; + private final Set transformationSpecifications; + private final Set globalConstraints; private final List objectives; private final Strategy strategy; public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, - LinkedHashSet transformationSpecifications, - LinkedHashSet globalConstraints, + Set transformationSpecifications, + Set globalConstraints, List objectives, Strategy strategy) { this.store = store; this.transformationSpecifications = transformationSpecifications; @@ -40,22 +39,26 @@ public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplor } @Override - public ModelAdapter createModelAdapter(Model model) { + public DesignSpaceExplorationAdapterImpl createModelAdapter(Model model) { return new DesignSpaceExplorationAdapterImpl(model, this); } - public LinkedHashSet getTransformationSpecifications() { + @Override + public Set getTransformationSpecifications() { return transformationSpecifications; } - public LinkedHashSet getGlobalConstraints() { + @Override + public Set getGlobalConstraints() { return globalConstraints; } + @Override public List getObjectives() { return objectives; } + @Override public Strategy getStrategy() { return strategy; } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java index 015d4815..8123c0d6 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java @@ -14,8 +14,7 @@ import tools.refinery.store.query.resultset.OrderedResultSet; import tools.refinery.store.query.resultset.ResultSet; import tools.refinery.store.tuple.Tuple; -import java.util.LinkedHashSet; -import java.util.Random; +import java.util.*; public class TransformationRule { @@ -66,11 +65,11 @@ public class TransformationRule { return precondition; } - public ResultSet getAllActivationsAsSets() { + public ResultSet getAllActivationsAsResultSet() { return activations; } - public LinkedHashSet getAllActivations() { + public Set getAllActivations() { var result = new LinkedHashSet(); var cursor = activations.getAll(); while (cursor.move()) { @@ -79,6 +78,15 @@ public class TransformationRule { return result; } + public List getAllActivationsAsList() { + var result = new ArrayList(); + var cursor = activations.getAll(); + while (cursor.move()) { + result.add(new Activation(this, cursor.getKey())); + } + return result; + } + public Activation getRandomActivation() { return new Activation(this, activations.getKey(random.nextInt(activations.size()))); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java index 8648864c..57f86401 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java @@ -26,6 +26,7 @@ public class BestFirstStrategy implements Strategy { private DesignSpaceExplorationAdapter dseAdapter; private int maxDepth; + private int maxSolutions; private boolean backTrackIfSolution = true; private boolean onlyBetterFirst = false; @@ -54,11 +55,20 @@ public class BestFirstStrategy implements Strategy { } public BestFirstStrategy(int maxDepth) { + this(maxDepth, -1); + } + + public BestFirstStrategy(int maxDepth, int maxSolutions) { if (maxDepth < 0) { this.maxDepth = Integer.MAX_VALUE; } else { this.maxDepth = maxDepth; } + if (maxSolutions < 0) { + this.maxSolutions = Integer.MAX_VALUE; + } else { + this.maxSolutions = maxSolutions; + } } public BestFirstStrategy continueIfHardObjectivesFulfilled() { @@ -76,12 +86,15 @@ public class BestFirstStrategy implements Strategy { this.dseAdapter = designSpaceExplorationAdapter; final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); - trajectoriesToExplore = new PriorityQueue(11, + trajectoriesToExplore = new PriorityQueue<>(11, (o1, o2) -> objectiveComparatorHelper.compare(o2.fitness, o1.fitness)); } @Override public void explore() { + if (maxSolutions == 0) { + return; + } final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); @@ -90,7 +103,7 @@ public class BestFirstStrategy implements Strategy { return; } - final Fitness firstFitness = dseAdapter.calculateFitness(); + final Fitness firstFitness = dseAdapter.getFitness(); if (firstFitness.isSatisfiesHardObjectives()) { dseAdapter.newSolution(); // "First state is a solution. Terminate."); @@ -103,9 +116,16 @@ public class BestFirstStrategy implements Strategy { return; } - final List firstTrajectory = dseAdapter.getTrajectory(); - TrajectoryWithFitness currentTrajectoryWithFitness = new TrajectoryWithFitness(firstTrajectory, firstFitness); - trajectoriesToExplore.add(currentTrajectoryWithFitness); +// final List firstTrajectory = dseAdapter.getTrajectory(); + +// TrajectoryWithFitness currentTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), +// firstFitness); +// trajectoriesToExplore.add(currentTrajectoryWithFitness); + + + var firstTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), firstFitness); + trajectoriesToExplore.add(firstTrajectoryWithFitness); + TrajectoryWithFitness currentTrajectoryWithFitness = null; mainLoop: while (true) { @@ -145,9 +165,13 @@ public class BestFirstStrategy implements Strategy { // "Global constraint is not satisfied."); dseAdapter.backtrack(); } else { - final Fitness nextFitness = dseAdapter.calculateFitness(); + final Fitness nextFitness = dseAdapter.getFitness(); if (nextFitness.isSatisfiesHardObjectives()) { dseAdapter.newSolution(); + var solutions = dseAdapter.getSolutions().size(); + if (solutions >= maxSolutions) { + return; + } // "Found a solution."); if (backTrackIfSolution) { dseAdapter.backtrack(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java index 1405789b..f4a0747a 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java @@ -21,6 +21,7 @@ public class DepthFirstStrategy implements Strategy { private DesignSpaceExplorationAdapter dseAdapter; private int maxDepth; + private int maxSolutions; private boolean backTrackIfSolution = true; public DepthFirstStrategy() { @@ -28,11 +29,20 @@ public class DepthFirstStrategy implements Strategy { } public DepthFirstStrategy(int maxDepth) { + this(maxDepth, -1); + } + + public DepthFirstStrategy(int maxDepth, int maxSolutions) { if (maxDepth < 0) { this.maxDepth = Integer.MAX_VALUE; } else { this.maxDepth = maxDepth; } + if (maxSolutions < 0) { + this.maxSolutions = Integer.MAX_VALUE; + } else { + this.maxSolutions = maxSolutions; + } } public DepthFirstStrategy continueIfHardObjectivesFulfilled() { @@ -47,6 +57,9 @@ public class DepthFirstStrategy implements Strategy { @Override public void explore() { + if (maxSolutions == 0) { + return; + } mainloop: while (true) { var globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); if (!globalConstraintsAreSatisfied) { @@ -61,9 +74,13 @@ public class DepthFirstStrategy implements Strategy { } } - Fitness fitness = dseAdapter.calculateFitness(); + Fitness fitness = dseAdapter.getFitness(); if (fitness.isSatisfiesHardObjectives()) { dseAdapter.newSolution(); + var solutions = dseAdapter.getSolutions().size(); + if (solutions >= maxSolutions) { + return; + } if (backTrackIfSolution) { var isSuccessfulUndo = dseAdapter.backtrack(); if (!isSuccessfulUndo) { @@ -76,7 +93,6 @@ public class DepthFirstStrategy implements Strategy { } } - var depth = dseAdapter.getDepth(); if (dseAdapter.getDepth() >= maxDepth) { var isSuccessfulUndo = dseAdapter.backtrack(); if (!isSuccessfulUndo) { diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java index 312bcebd..f57f68ef 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java @@ -137,8 +137,8 @@ public class TransformationRuleTest { queryEngine.flushChanges(); - var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsSets(); - var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsSets(); + var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsResultSet(); + var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsResultSet(); assertResults(Map.of( Tuple.of(newClass1Id, newFieldId), true, @@ -236,12 +236,12 @@ public class TransformationRuleTest { assertResults(Map.of( Tuple.of(newModelId, newClass1Id), true, Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule0.getAllActivationsAsSets()); + ), deleteEmptyClassRule0.getAllActivationsAsResultSet()); assertResults(Map.of( Tuple.of(newModelId, newClass1Id), true, Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule1.getAllActivationsAsSets()); + ), deleteEmptyClassRule1.getAllActivationsAsResultSet()); assertEquals(Tuple.of(newModelId, newClass2Id), activation0); assertEquals(Tuple.of(newModelId, newClass1Id), activation1); @@ -312,7 +312,7 @@ public class TransformationRuleTest { assertResults(Map.of( Tuple.of(newModelId, newClass1Id), true, Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsSets()); + ), deleteEmptyClassRule.getAllActivationsAsResultSet()); deleteEmptyClassRule.fireActivation(Tuple.of(0, 1)); @@ -320,7 +320,7 @@ public class TransformationRuleTest { assertResults(Map.of( Tuple.of(newModelId, newClass1Id), false, Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsSets()); + ), deleteEmptyClassRule.getAllActivationsAsResultSet()); } @Test @@ -388,21 +388,21 @@ public class TransformationRuleTest { assertResults(Map.of( Tuple.of(newModelId, newClass1Id), true, Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsSets()); + ), deleteEmptyClassRule.getAllActivationsAsResultSet()); deleteEmptyClassRule.fireRandomActivation(); assertResults(Map.of( Tuple.of(newModelId, newClass1Id), true, Tuple.of(newModelId, newClass2Id), false - ), deleteEmptyClassRule.getAllActivationsAsSets()); + ), deleteEmptyClassRule.getAllActivationsAsResultSet()); deleteEmptyClassRule.fireRandomActivation(); assertResults(Map.of( Tuple.of(newModelId, newClass1Id), false, Tuple.of(newModelId, newClass2Id), false - ), deleteEmptyClassRule.getAllActivationsAsSets()); + ), deleteEmptyClassRule.getAllActivationsAsResultSet()); } } -- cgit v1.2.3-54-g00ecf From a20fd33c647d8511762f84436dbd8d1632b57fe8 Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Mon, 7 Aug 2023 12:59:35 +0200 Subject: Clean up design space exploration --- .../store/dse/DesignSpaceExplorationAdapter.java | 2 - .../DesignSpaceExplorationAdapterImpl.java | 7 +-- .../store/dse/objectives/BaseObjective.java | 2 +- .../refinery/store/dse/objectives/Comparators.java | 2 +- .../refinery/store/dse/objectives/Fitness.java | 15 +++++ .../dse/objectives/ObjectiveComparatorHelper.java | 1 + .../store/dse/strategy/BestFirstStrategy.java | 65 +++++++--------------- .../store/dse/strategy/DepthFirstStrategy.java | 23 +++----- .../tools/refinery/store/dse/CRAExamplesTest.java | 2 +- .../java/tools/refinery/store/dse/DebugTest.java | 2 +- .../store/dse/DesignSpaceExplorationTest.java | 2 +- .../refinery/store/dse/TransformationRuleTest.java | 2 +- .../visualization/ModelVisualizerStoreAdapter.java | 1 - .../internal/ModelVisualizerAdapterImpl.java | 57 ++++++++++++------- 14 files changed, 89 insertions(+), 94 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java index c4aa97c2..c45f088a 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java @@ -52,8 +52,6 @@ public interface DesignSpaceExplorationAdapter extends ModelAdapter { public void fireRandomActivation(); - public boolean isCurrentInTrajectory(); - public List getTrajectory(); public boolean isCurrentStateAlreadyTraversed(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java index 4e5cc467..1329480c 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -43,7 +43,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration private ObjectiveComparatorHelper objectiveComparatorHelper; private List trajectory = new ArrayList<>(); private Map parents = new HashMap<>(); - private Fitness lastFitness; private final List solutions = new ArrayList<>(); private Map> statesAndTraversedActivations; private Random random = new Random(); @@ -184,9 +183,7 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration @Override public Fitness getFitness() { - var result = fitnessCache.computeIfAbsent(model.getState(), s -> calculateFitness()); - lastFitness = result; - return result; + return fitnessCache.computeIfAbsent(model.getState(), s -> calculateFitness()); } private Fitness calculateFitness() { @@ -201,8 +198,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } result.setSatisfiesHardObjectives(satisfiesHardObjectives); - lastFitness = result; - return result; } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java index 7df33efe..b76598fb 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java @@ -30,7 +30,7 @@ public abstract class BaseObjective implements Objective { protected boolean isThereFitnessConstraint = false; protected Comparator fitnessConstraintComparator; - public BaseObjective(String name) { + protected BaseObjective(String name) { Objects.requireNonNull(name, "Name of the objective cannot be null."); this.name = name; } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java index 476504b0..181397b3 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java @@ -15,7 +15,7 @@ public class Comparators { private Comparators() { /*Utility class constructor*/ } - public static final Comparator HIGHER_IS_BETTER = (o1, o2) -> o1.compareTo(o2); + public static final Comparator HIGHER_IS_BETTER = Double::compareTo; public static final Comparator LOWER_IS_BETTER = (o1, o2) -> o2.compareTo(o1); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java index 92709d3e..0bf956d2 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java @@ -27,4 +27,19 @@ public class Fitness extends HashMap { public String toString() { return super.toString() + " hardObjectives=" + satisfiesHardObjectives; } + + @Override + public boolean equals(Object other) { + if (other == null) return false; + if (getClass() != other.getClass()) return false; + return satisfiesHardObjectives == ((Fitness) other).satisfiesHardObjectives; + } + + @Override + public int hashCode() { + int h = super.hashCode(); + h = h * 31 + (satisfiesHardObjectives ? 1 : 0); + return h; + } + } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java index 1d676562..eb03eeaf 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java @@ -49,6 +49,7 @@ public class ObjectiveComparatorHelper { } } if (o2HasBetterFitness) { + return -1; } else if (o1HasBetterFitness) { return 1; } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java index 57f86401..98af5695 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java @@ -25,29 +25,18 @@ public class BestFirstStrategy implements Strategy { private DesignSpaceExplorationAdapter dseAdapter; - private int maxDepth; - private int maxSolutions; + private final int maxDepth; + private final int maxSolutions; private boolean backTrackIfSolution = true; private boolean onlyBetterFirst = false; private PriorityQueue trajectoriesToExplore; - private static class TrajectoryWithFitness { - - public List trajectory; - public Fitness fitness; - - public TrajectoryWithFitness(List trajectory, Fitness fitness) { - super(); - this.trajectory = trajectory; - this.fitness = fitness; - } - + private record TrajectoryWithFitness(List trajectory, Fitness fitness) { @Override public String toString() { - return trajectory.toString() + fitness.toString(); - } - + return trajectory.toString() + fitness.toString(); + } } public BestFirstStrategy() { @@ -99,14 +88,14 @@ public class BestFirstStrategy implements Strategy { boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); if (!globalConstraintsAreSatisfied) { - // "Global constraint is not satisfied in the first state. Terminate."); + // Global constraint is not satisfied in the first state. Terminate. return; } final Fitness firstFitness = dseAdapter.getFitness(); if (firstFitness.isSatisfiesHardObjectives()) { dseAdapter.newSolution(); - // "First state is a solution. Terminate."); + // First state is a solution. Terminate. if (backTrackIfSolution) { return; } @@ -116,12 +105,6 @@ public class BestFirstStrategy implements Strategy { return; } -// final List firstTrajectory = dseAdapter.getTrajectory(); - -// TrajectoryWithFitness currentTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), -// firstFitness); -// trajectoriesToExplore.add(currentTrajectoryWithFitness); - var firstTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), firstFitness); trajectoriesToExplore.add(firstTrajectoryWithFitness); @@ -131,13 +114,11 @@ public class BestFirstStrategy implements Strategy { if (currentTrajectoryWithFitness == null) { if (trajectoriesToExplore.isEmpty()) { - // "State space is fully traversed."); + // State space is fully traversed. return; } else { currentTrajectoryWithFitness = trajectoriesToExplore.element(); -// if (logger.isDebugEnabled()) { -// "New trajectory is chosen: " + currentTrajectoryWithFitness); -// } + // New trajectory is chosen: " + currentTrajectoryWithFitness dseAdapter.restoreTrajectory(currentTrajectoryWithFitness.trajectory); } } @@ -150,19 +131,17 @@ public class BestFirstStrategy implements Strategy { while (iterator.hasNext()) { final Activation nextActivation = iterator.next(); if (!iterator.hasNext()) { - // "Last untraversed activation of the state."); + // Last untraversed activation of the state. trajectoriesToExplore.remove(currentTrajectoryWithFitness); } -// if (logger.isDebugEnabled()) { -// "Executing new activation: " + nextActivation); -// } + // Executing new activation dseAdapter.fireActivation(nextActivation); if (dseAdapter.isCurrentStateAlreadyTraversed()) { - // "The new state is already visited."); + // The new state is already visited. dseAdapter.backtrack(); } else if (!dseAdapter.checkGlobalConstraints()) { - // "Global constraint is not satisfied."); + // Global constraint is not satisfied. dseAdapter.backtrack(); } else { final Fitness nextFitness = dseAdapter.getFitness(); @@ -172,14 +151,14 @@ public class BestFirstStrategy implements Strategy { if (solutions >= maxSolutions) { return; } - // "Found a solution."); + // Found a solution. if (backTrackIfSolution) { dseAdapter.backtrack(); continue; } } if (dseAdapter.getDepth() >= maxDepth) { - // "Reached max depth."); + // Reached max depth. dseAdapter.backtrack(); continue; } @@ -191,33 +170,31 @@ public class BestFirstStrategy implements Strategy { int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness, nextTrajectoryWithFitness.fitness); if (compare < 0) { - // "Better fitness, moving on: " + nextFitness); + // Better fitness, moving on currentTrajectoryWithFitness = nextTrajectoryWithFitness; continue mainLoop; } else if (compare == 0) { if (onlyBetterFirst) { - // "Equally good fitness, backtrack: " + nextFitness); + // Equally good fitness, backtrack dseAdapter.backtrack(); - continue; } else { - // "Equally good fitness, moving on: " + nextFitness); + // Equally good fitness, moving on currentTrajectoryWithFitness = nextTrajectoryWithFitness; continue mainLoop; } } else { - // "Worse fitness."); + //"Worse fitness currentTrajectoryWithFitness = null; continue mainLoop; } } } - // "State is fully traversed."); + // State is fully traversed. trajectoriesToExplore.remove(currentTrajectoryWithFitness); currentTrajectoryWithFitness = null; } - // "Interrupted."); - + // Interrupted. } } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java index f4a0747a..15529371 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java @@ -65,11 +65,11 @@ public class DepthFirstStrategy implements Strategy { if (!globalConstraintsAreSatisfied) { var isSuccessfulUndo = dseAdapter.backtrack(); if (!isSuccessfulUndo) { -// "Global constraint is not satisfied and cannot backtrack." + // Global constraint is not satisfied and cannot backtrack. break; } else { -// "Global constraint is not satisfied, backtrack." + // Global constraint is not satisfied, backtrack. continue; } } @@ -84,10 +84,10 @@ public class DepthFirstStrategy implements Strategy { if (backTrackIfSolution) { var isSuccessfulUndo = dseAdapter.backtrack(); if (!isSuccessfulUndo) { -// "Found a solution but cannot backtrack." + // Found a solution but cannot backtrack. break; } else { -// "Found a solution, backtrack." + // Found a solution, backtrack. continue; } } @@ -96,7 +96,7 @@ public class DepthFirstStrategy implements Strategy { if (dseAdapter.getDepth() >= maxDepth) { var isSuccessfulUndo = dseAdapter.backtrack(); if (!isSuccessfulUndo) { -// "Reached max depth but cannot backtrack." + // Reached max depth but cannot backtrack. break; } } @@ -104,16 +104,11 @@ public class DepthFirstStrategy implements Strategy { Collection activations; do { activations = dseAdapter.getUntraversedActivations(); - if (activations.isEmpty()) { - if (!dseAdapter.backtrack()) { - // "No more transitions from current state and cannot backtrack." - break mainloop; - } - else { - // "No more transitions from current state, backtrack." - continue; - } + if (activations.isEmpty() && !dseAdapter.backtrack()) { + // No more transitions from current state and cannot backtrack. + break mainloop; } + // No more transitions from current state, backtrack. } while (activations.isEmpty()); dseAdapter.fireRandomActivation(); diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index 4bdb05ec..f1e90280 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -25,7 +25,7 @@ import java.util.List; import static tools.refinery.store.query.literal.Literals.not; -public class CRAExamplesTest { +class CRAExamplesTest { private static final Symbol name = Symbol.of("Name", 1, String.class); // private static final Symbol classModel = Symbol.of("ClassModel", 1); diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index 101a5362..553510c8 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -20,7 +20,7 @@ import tools.refinery.store.tuple.Tuple; import tools.refinery.visualization.ModelVisualizerAdapter; import tools.refinery.visualization.internal.FileFormat; -public class DebugTest { +class DebugTest { private static final Symbol classModel = Symbol.of("ClassModel", 1); private static final Symbol classElement = Symbol.of("ClassElement", 1); private static final Symbol feature = Symbol.of("Feature", 1); diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java index f4644407..a379835d 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java @@ -21,7 +21,7 @@ import tools.refinery.visualization.ModelVisualizerAdapter; import static org.junit.jupiter.api.Assertions.assertEquals; -public class DesignSpaceExplorationTest { +class DesignSpaceExplorationTest { // private static final Symbol namedElement = Symbol.of("NamedElement", 1); // private static final Symbol attribute = Symbol.of("Attribute", 1); // private static final Symbol method = Symbol.of("Method", 1); diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java index f57f68ef..3aa4c92e 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java @@ -23,7 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static tools.refinery.store.query.literal.Literals.not; import static tools.refinery.store.dse.tests.QueryAssertions.assertResults; -public class TransformationRuleTest { +class TransformationRuleTest { private static final Symbol classModel = Symbol.of("ClassModel", 1); private static final Symbol classElement = Symbol.of("ClassElement", 1); diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java index 6599d4c3..6e6453fd 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java @@ -6,7 +6,6 @@ package tools.refinery.visualization; import tools.refinery.store.adapter.ModelStoreAdapter; -import tools.refinery.store.query.ModelQueryStoreAdapter; public interface ModelVisualizerStoreAdapter extends ModelStoreAdapter { } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java index 06cc8113..efafe5a2 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -21,38 +21,39 @@ import java.util.stream.Collectors; public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { private final Model model; private final ModelVisualizerStoreAdapter storeAdapter; - private final Map> interpretations; + private final Map> allInterpretations; private final StringBuilder designSpaceBuilder = new StringBuilder(); private final Map states = new HashMap<>(); private int transitionCounter = 0; private Integer numberOfStates = 0; - private static final Map truthValueToDot = new HashMap<>() - {{ - put(TruthValue.TRUE, "1"); - put(TruthValue.FALSE, "0"); - put(TruthValue.UNKNOWN, "½"); - put(TruthValue.ERROR, "E"); - put(true, "1"); - put(false, "0"); - }}; + private static final Map truthValueToDot = Map.of( + TruthValue.TRUE, "1", + TruthValue.FALSE, "0", + TruthValue.UNKNOWN, "½", + TruthValue.ERROR, "E", + true, "1", + false, "0" + ); public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { this.model = model; this.storeAdapter = storeAdapter; - this.interpretations = new HashMap<>(); + this.allInterpretations = new HashMap<>(); for (var symbol : storeAdapter.getStore().getSymbols()) { var arity = symbol.arity(); if (arity < 1 || arity > 2) { continue; } var interpretation = (Interpretation) model.getInterpretation(symbol); - interpretations.put(symbol, interpretation); + allInterpretations.put(symbol, interpretation); } designSpaceBuilder.append("digraph designSpace {\n"); designSpaceBuilder.append(""" + nodesep=0 + ranksep=5 node[ - style=filled - fillcolor=white + \tstyle=filled + \tfillcolor=white ] """); } @@ -90,7 +91,7 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { ] """); - for (var entry : interpretations.entrySet()) { + for (var entry : allInterpretations.entrySet()) { var key = entry.getKey(); var arity = key.arity(); var cursor = entry.getValue().getAll(); @@ -302,6 +303,20 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { designSpaceBuilder.append(states.get(state)).append(" [URL=\"./").append(states.get(state)).append(".svg\"]\n"); } + @Override + public void addState(Version state, Collection fitness) { + if (states.containsKey(state)) { + return; + } + states.put(state, numberOfStates++); + designSpaceBuilder.append(states.get(state)).append(" [label = \"").append(states.get(state)).append(" ("); + + for (var f : fitness) { + designSpaceBuilder.append(f).append(", "); + } + designSpaceBuilder.append(")\"\n").append("URL=\"./").append(states.get(state)).append(".svg\"]\n"); + } + @Override public void addSolution(Version state) { addState(state); @@ -329,12 +344,12 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { @Override public boolean renderDesignSpace(String path, FileFormat format) { - for (var entry : states.entrySet()) { - var stateId = entry.getValue(); - var stateDot = createDotForModelState(entry.getKey()); - saveDot(stateDot, path + "/" + stateId + ".dot"); - renderDot(stateDot, format, path + "/" + stateId + "." + format.getFormat()); - } +// for (var entry : states.entrySet()) { +// var stateId = entry.getValue(); +// var stateDot = createDotForModelState(entry.getKey()); +// saveDot(stateDot, path + "/" + stateId + ".dot"); +// renderDot(stateDot, format, path + "/" + stateId + "." + format.getFormat()); +// } var designSpaceDot = buildDesignSpaceDot(); saveDot(designSpaceDot, path + "/designSpace.dot"); return renderDot(designSpaceDot, format, path + "/designSpace." + format.getFormat()); -- cgit v1.2.3-54-g00ecf From d5d8ad817c2f7d348a1fb40ca3f627ccc4ea37d5 Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Mon, 7 Aug 2023 15:10:07 +0200 Subject: Update visualizer builder --- .../DesignSpaceExplorationAdapterImpl.java | 10 +-- .../AlwaysSatisfiedRandomHardObjective.java | 56 ++++++++++++++ .../tools/refinery/store/dse/CRAExamplesTest.java | 11 ++- .../java/tools/refinery/store/dse/DebugTest.java | 11 ++- .../visualization/ModelVisualizerAdapter.java | 22 ++---- .../visualization/ModelVisualizerBuilder.java | 5 ++ .../visualization/ModelVisualizerStoreAdapter.java | 11 +++ .../internal/ModelVisualizeStoreAdapterImpl.java | 33 +++++++- .../internal/ModelVisualizerAdapterImpl.java | 88 +++++++++++++++------- .../internal/ModelVisualizerBuilderImpl.java | 38 +++++++++- 10 files changed, 223 insertions(+), 62 deletions(-) create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java index 1329480c..008b2dab 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -96,6 +96,9 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration trajectory.add(state); strategy.initStrategy(this); strategy.explore(); + if (isVisualizationEnabled) { + modelVisualizerAdapter.visualize(); + } return solutions; } @@ -247,7 +250,7 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration isNewState = !statesAndTraversedActivations.containsKey(newState); if (isVisualizationEnabled) { if (isNewState) { - modelVisualizerAdapter.addState(newState); + modelVisualizerAdapter.addState(newState, getFitness().values()); } // TODO: Change to this: modelVisualizerAdapter.addTransition(previousState, newState, activation.transformationRule().getName(), @@ -272,11 +275,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration fireActivation(activationId); } - @Override - public boolean isCurrentInTrajectory() { - return trajectory.contains(model.getState()); - } - public List getAllActivations() { List result = new LinkedList<>(); for (var rule : transformationRules) { diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java new file mode 100644 index 00000000..327d5e2f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro + * Copyright (c) 2023 The Refinery Authors + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.store.dse.objectives; + +import tools.refinery.store.dse.DesignSpaceExplorationAdapter; + +import java.util.Random; + +/** + * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution. + * + * @author Andras Szabolcs Nagy + * + */ +public class AlwaysSatisfiedRandomHardObjective extends BaseObjective { + + private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective"; + private static final Random random = new Random(0); + + public AlwaysSatisfiedRandomHardObjective() { + super(DEFAULT_NAME); + } + + public AlwaysSatisfiedRandomHardObjective(String name) { + super(name); + } + + @Override + public Double getFitness(DesignSpaceExplorationAdapter context) { +// return 0d; + return random.nextDouble(); + } + + @Override + public boolean isHardObjective() { + return true; + } + + @Override + public boolean satisfiesHardObjective(Double fitness) { + return true; + } + + @Override + public Objective createNew() { + return this; + } + +} diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index f1e90280..23f0e703 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -188,7 +188,13 @@ class CRAExamplesTest { .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, moveFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder()) + .with(ModelVisualizerAdapter.builder() + .withOutputpath("test_output") + .withFormat(FileFormat.DOT) + .withFormat(FileFormat.SVG) + .saveStates() + .saveDesignSpace() + ) .with(DesignSpaceExplorationAdapter.builder() .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) // .strategy(new DepthFirstStrategy(3).continueIfHardObjectivesFulfilled() @@ -271,9 +277,6 @@ class CRAExamplesTest { var states = dseAdapter.explore(); System.out.println("states size: " + states.size()); - System.out.println("states: " + states); - var visualizer = model.getAdapter(ModelVisualizerAdapter.class); - visualizer.renderDesignSpace("test_output", FileFormat.SVG); } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index 553510c8..88c4f152 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -90,7 +90,13 @@ class DebugTest { .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) .with(ViatraModelQueryAdapter.builder() .queries(createClassPrecondition, createFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder()) + .with(ModelVisualizerAdapter.builder() + .withOutputpath("test_output") + .withFormat(FileFormat.DOT) + .withFormat(FileFormat.SVG) + .saveStates() + .saveDesignSpace() + ) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule, createFeatureRule) .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() @@ -113,10 +119,7 @@ class DebugTest { var states = dseAdapter.explore(); - var visualizer = model.getAdapter(ModelVisualizerAdapter.class); - visualizer.renderDesignSpace("test_output", FileFormat.SVG); System.out.println("states size: " + states.size()); - System.out.println("states: " + states); } } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java index bc32323d..ae87d8ac 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerAdapter.java @@ -8,9 +8,10 @@ package tools.refinery.visualization; import tools.refinery.store.adapter.ModelAdapter; import tools.refinery.store.map.Version; import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.internal.FileFormat; import tools.refinery.visualization.internal.ModelVisualizerBuilderImpl; +import java.util.Collection; + public interface ModelVisualizerAdapter extends ModelAdapter { ModelVisualizerStoreAdapter getStoreAdapter(); @@ -18,27 +19,14 @@ public interface ModelVisualizerAdapter extends ModelAdapter { return new ModelVisualizerBuilderImpl(); } - public String createDotForCurrentModelState(); - - public String createDotForModelState(Version version); - - public boolean saveDot(String dot, String filePath); - - public boolean renderDot(String dot, String filePath); - - public boolean renderDot(String dot, FileFormat format, String filePath); - public void addTransition(Version from, Version to, String action); public void addTransition(Version from, Version to, String action, Tuple activation); public void addState(Version state); + public void addState(Version state, Collection fitness); + public void addState(Version state, String label); public void addSolution(Version state); - - public boolean saveDesignSpace(String path); - - public boolean renderDesignSpace(String path); - - public boolean renderDesignSpace(String path, FileFormat format); + public void visualize(); } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java index 56cc425e..592f5fcf 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerBuilder.java @@ -6,6 +6,11 @@ package tools.refinery.visualization; import tools.refinery.store.adapter.ModelAdapterBuilder; +import tools.refinery.visualization.internal.FileFormat; public interface ModelVisualizerBuilder extends ModelAdapterBuilder { + ModelVisualizerBuilder withOutputpath(String outputpath); + ModelVisualizerBuilder withFormat(FileFormat format); + ModelVisualizerBuilder saveDesignSpace(); + ModelVisualizerBuilder saveStates(); } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java index 6e6453fd..46663b2a 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/ModelVisualizerStoreAdapter.java @@ -6,6 +6,17 @@ package tools.refinery.visualization; import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.visualization.internal.FileFormat; + +import java.util.Set; public interface ModelVisualizerStoreAdapter extends ModelStoreAdapter { + + String getOutputPath(); + + boolean isRenderDesignSpace(); + + boolean isRenderStates(); + + Set getFormats(); } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java index b4db2682..04be22d6 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizeStoreAdapterImpl.java @@ -10,11 +10,22 @@ import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.visualization.ModelVisualizerStoreAdapter; +import java.util.Set; + public class ModelVisualizeStoreAdapterImpl implements ModelVisualizerStoreAdapter { private final ModelStore store; + private final String outputPath; + private final boolean renderDesignSpace; + private final boolean renderStates; + private final Set formats; - public ModelVisualizeStoreAdapterImpl(ModelStore store) { + public ModelVisualizeStoreAdapterImpl(ModelStore store, String outputPath, Set formats, + boolean renderDesignSpace, boolean renderStates) { this.store = store; + this.outputPath = outputPath; + this.formats = formats; + this.renderDesignSpace = renderDesignSpace; + this.renderStates = renderStates; } @Override @@ -26,4 +37,24 @@ public class ModelVisualizeStoreAdapterImpl implements ModelVisualizerStoreAdapt public ModelAdapter createModelAdapter(Model model) { return new ModelVisualizerAdapterImpl(model, this); } + + @Override + public String getOutputPath() { + return outputPath; + } + + @Override + public boolean isRenderDesignSpace() { + return renderDesignSpace; + } + + @Override + public boolean isRenderStates() { + return renderStates; + } + + @Override + public Set getFormats() { + return formats; + } } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java index efafe5a2..531969b4 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerAdapterImpl.java @@ -26,6 +26,11 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { private final Map states = new HashMap<>(); private int transitionCounter = 0; private Integer numberOfStates = 0; + private final String outputPath; + private final Set formats; + private final boolean renderDesignSpace; + private final boolean renderStates; + private static final Map truthValueToDot = Map.of( TruthValue.TRUE, "1", TruthValue.FALSE, "0", @@ -38,6 +43,14 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { public ModelVisualizerAdapterImpl(Model model, ModelVisualizerStoreAdapter storeAdapter) { this.model = model; this.storeAdapter = storeAdapter; + this.outputPath = storeAdapter.getOutputPath(); + this.formats = storeAdapter.getFormats(); + if (formats.isEmpty()) { + formats.add(FileFormat.SVG); + } + this.renderDesignSpace = storeAdapter.isRenderDesignSpace(); + this.renderStates = storeAdapter.isRenderStates(); + this.allInterpretations = new HashMap<>(); for (var symbol : storeAdapter.getStore().getSymbols()) { var arity = symbol.arity(); @@ -68,8 +81,7 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return storeAdapter; } - @Override - public String createDotForCurrentModelState() { + private String createDotForCurrentModelState() { var unaryTupleToInterpretationsMap = new HashMap>>(); @@ -229,8 +241,7 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { }; } - @Override - public String createDotForModelState(Version version) { + private String createDotForModelState(Version version) { var currentVersion = model.getState(); model.restore(version); var graph = createDotForCurrentModelState(); @@ -238,8 +249,7 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return graph; } - @Override - public boolean saveDot(String dot, String filePath) { + private boolean saveDot(String dot, String filePath) { File file = new File(filePath); file.getParentFile().mkdirs(); @@ -252,13 +262,11 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return true; } - @Override - public boolean renderDot(String dot, String filePath) { + private boolean renderDot(String dot, String filePath) { return renderDot(dot, FileFormat.SVG, filePath); } - @Override - public boolean renderDot(String dot, FileFormat format, String filePath) { + private boolean renderDot(String dot, FileFormat format, String filePath) { try { Process process = new ProcessBuilder("dot", "-T" + format.getFormat(), "-o", filePath).start(); @@ -305,15 +313,21 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { @Override public void addState(Version state, Collection fitness) { + var labelBuilder = new StringBuilder(); + for (var f : fitness) { + labelBuilder.append(f).append(", "); + } + addState(state, labelBuilder.toString()); + } + + @Override + public void addState(Version state, String label) { if (states.containsKey(state)) { return; } states.put(state, numberOfStates++); designSpaceBuilder.append(states.get(state)).append(" [label = \"").append(states.get(state)).append(" ("); - - for (var f : fitness) { - designSpaceBuilder.append(f).append(", "); - } + designSpaceBuilder.append(label); designSpaceBuilder.append(")\"\n").append("URL=\"./").append(states.get(state)).append(".svg\"]\n"); } @@ -328,8 +342,7 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return designSpaceBuilder.toString(); } - @Override - public boolean saveDesignSpace(String path) { + private boolean saveDesignSpace(String path) { saveDot(buildDesignSpaceDot(), path + "/designSpace.dot"); for (var entry : states.entrySet()) { saveDot(createDotForModelState(entry.getKey()), path + "/" + entry.getValue() + ".dot"); @@ -337,21 +350,38 @@ public class ModelVisualizerAdapterImpl implements ModelVisualizerAdapter { return true; } - @Override - public boolean renderDesignSpace(String path) { - return renderDesignSpace(path, FileFormat.SVG); + private void renderDesignSpace(String path, Set formats) { + File filePath = new File(path); + filePath.mkdirs(); + if (renderStates) { + for (var entry : states.entrySet()) { + var stateId = entry.getValue(); + var stateDot = createDotForModelState(entry.getKey()); + for (var format : formats) { + if (format == FileFormat.DOT) { + saveDot(stateDot, path + "/" + stateId + ".dot"); + } + else { + renderDot(stateDot, format, path + "/" + stateId + "." + format.getFormat()); + } + } + } + } + if (renderDesignSpace) { + var designSpaceDot = buildDesignSpaceDot(); + for (var format : formats) { + if (format == FileFormat.DOT) { + saveDot(designSpaceDot, path + "/designSpace.dot"); + } + else { + renderDot(designSpaceDot, format, path + "/designSpace." + format.getFormat()); + } + } + } } @Override - public boolean renderDesignSpace(String path, FileFormat format) { -// for (var entry : states.entrySet()) { -// var stateId = entry.getValue(); -// var stateDot = createDotForModelState(entry.getKey()); -// saveDot(stateDot, path + "/" + stateId + ".dot"); -// renderDot(stateDot, format, path + "/" + stateId + "." + format.getFormat()); -// } - var designSpaceDot = buildDesignSpaceDot(); - saveDot(designSpaceDot, path + "/designSpace.dot"); - return renderDot(designSpaceDot, format, path + "/designSpace." + format.getFormat()); + public void visualize() { + renderDesignSpace(outputPath, formats); } } diff --git a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java index d19cf2cf..e4d801d8 100644 --- a/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java +++ b/subprojects/visualization/src/main/java/tools/refinery/visualization/internal/ModelVisualizerBuilderImpl.java @@ -9,11 +9,47 @@ import tools.refinery.store.adapter.AbstractModelAdapterBuilder; import tools.refinery.store.model.ModelStore; import tools.refinery.visualization.ModelVisualizerBuilder; +import java.util.LinkedHashSet; +import java.util.Set; + public class ModelVisualizerBuilderImpl extends AbstractModelAdapterBuilder implements ModelVisualizerBuilder { + private String outputPath; + private boolean saveDesignSpace = false; + private boolean saveStates = false; + private Set formats = new LinkedHashSet<>(); + @Override protected ModelVisualizeStoreAdapterImpl doBuild(ModelStore store) { - return new ModelVisualizeStoreAdapterImpl(store); + return new ModelVisualizeStoreAdapterImpl(store, outputPath, formats, saveDesignSpace, saveStates); + } + + @Override + public ModelVisualizerBuilder withOutputpath(String outputpath) { + checkNotConfigured(); + this.outputPath = outputpath; + return this; + } + + @Override + public ModelVisualizerBuilder withFormat(FileFormat format) { + checkNotConfigured(); + this.formats.add(format); + return this; + } + + @Override + public ModelVisualizerBuilder saveDesignSpace() { + checkNotConfigured(); + this.saveDesignSpace = true; + return this; + } + + @Override + public ModelVisualizerBuilder saveStates() { + checkNotConfigured(); + this.saveStates = true; + return this; } } -- cgit v1.2.3-54-g00ecf From 52bd73128478c44bcbeb49ba3b8c971976053bcd Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Mon, 7 Aug 2023 15:10:36 +0200 Subject: Add dummy objective with random fitness --- .../src/test/java/tools/refinery/store/dse/CRAExamplesTest.java | 2 ++ .../store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java | 2 ++ 2 files changed, 4 insertions(+) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index 23f0e703..2fe9fec3 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -6,6 +6,7 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; +import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; @@ -197,6 +198,7 @@ class CRAExamplesTest { ) .with(DesignSpaceExplorationAdapter.builder() .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) + .objectives(new AlwaysSatisfiedRandomHardObjective()) // .strategy(new DepthFirstStrategy(3).continueIfHardObjectivesFulfilled() .strategy(new BestFirstStrategy(6).continueIfHardObjectivesFulfilled() // .goOnOnlyIfFitnessIsBetter() diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index 88c4f152..2cb59ec7 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -6,6 +6,7 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; +import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; @@ -99,6 +100,7 @@ class DebugTest { ) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule, createFeatureRule) + .objectives(new AlwaysSatisfiedRandomHardObjective()) .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() // .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() // .goOnOnlyIfFitnessIsBetter() -- cgit v1.2.3-54-g00ecf From 9c0f8f8a28aaaec01a6177aacd0c4dddd8a2b7ed Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Mon, 7 Aug 2023 15:16:51 +0200 Subject: Disable tests --- .../src/test/java/tools/refinery/store/dse/CRAExamplesTest.java | 2 ++ .../store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java | 2 ++ 2 files changed, 4 insertions(+) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index 2fe9fec3..d761db02 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -5,6 +5,7 @@ */ package tools.refinery.store.dse; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; import tools.refinery.store.model.ModelStore; @@ -181,6 +182,7 @@ class CRAExamplesTest { }); @Test + @Disabled("This test is only for debugging purposes") void craTest() { var store = ModelStore.builder() .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index 2cb59ec7..36bc6cce 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -5,6 +5,7 @@ */ package tools.refinery.store.dse; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; import tools.refinery.store.model.ModelStore; @@ -42,6 +43,7 @@ class DebugTest { @Test + @Disabled("This test is only for debugging purposes") void BFSTest() { var createClassPrecondition = Query.of("CreateClassPrecondition", (builder, model) -> builder.clause( -- cgit v1.2.3-54-g00ecf From 31447338dede415158b017fd78a6b6036cddc511 Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Mon, 7 Aug 2023 15:26:37 +0200 Subject: Remove visualization from DSE tests --- .../tools/refinery/store/dse/DesignSpaceExplorationTest.java | 9 --------- 1 file changed, 9 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java index a379835d..89adbf65 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java @@ -17,7 +17,6 @@ import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.ModelVisualizerAdapter; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -118,7 +117,6 @@ class DesignSpaceExplorationTest { var store = ModelStore.builder() .symbols(classModel) .with(ViatraModelQueryAdapter.builder()) - .with(ModelVisualizerAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .strategy(new DepthFirstStrategy(0))) .build(); @@ -158,7 +156,6 @@ class DesignSpaceExplorationTest { .symbols(classModel, classElement, classes) .with(ViatraModelQueryAdapter.builder() .queries(createClassPrecondition)) - .with(ModelVisualizerAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule) .strategy(new DepthFirstStrategy(4) @@ -205,7 +202,6 @@ class DesignSpaceExplorationTest { .symbols(classModel, classElement, classes) .with(ViatraModelQueryAdapter.builder() .queries(createClassPrecondition)) - .with(ModelVisualizerAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule) .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() @@ -274,7 +270,6 @@ class DesignSpaceExplorationTest { .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) .with(ViatraModelQueryAdapter.builder() .queries(createClassPrecondition, createFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule, createFeatureRule) .strategy(new DepthFirstStrategy(10).continueIfHardObjectivesFulfilled() @@ -298,7 +293,6 @@ class DesignSpaceExplorationTest { var store = ModelStore.builder() .symbols(classModel) .with(ViatraModelQueryAdapter.builder()) - .with(ModelVisualizerAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .strategy(new BestFirstStrategy(0))) .build(); @@ -338,7 +332,6 @@ class DesignSpaceExplorationTest { .symbols(classModel, classElement, classes) .with(ViatraModelQueryAdapter.builder() .queries(createClassPrecondition)) - .with(ModelVisualizerAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule) .strategy(new BestFirstStrategy(4) @@ -385,7 +378,6 @@ class DesignSpaceExplorationTest { .symbols(classModel, classElement, classes) .with(ViatraModelQueryAdapter.builder() .queries(createClassPrecondition)) - .with(ModelVisualizerAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule) .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() @@ -454,7 +446,6 @@ class DesignSpaceExplorationTest { .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) .with(ViatraModelQueryAdapter.builder() .queries(createClassPrecondition, createFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule, createFeatureRule) .strategy(new BestFirstStrategy(10).continueIfHardObjectivesFulfilled() -- cgit v1.2.3-54-g00ecf From 36550e2be2146b290210cb12c76d0341e499f849 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Mon, 7 Aug 2023 16:07:09 +0200 Subject: Tests + small changes for AbstractNeighbourhoodCalculator and StateEquivalenceCheckerImpl --- .../AbstractNeighbourhoodCalculator.java | 15 +- .../statecoding/neighbourhood/ObjectCodeImpl.java | 3 +- .../StateEquivalenceCheckerImpl.java | 19 +- .../store/statecoding/EquivalenceTest.java | 178 +++++++++++++++++++ .../store/statecoding/StateCoderUnitTest.java | 195 +++++++++++++++++++++ 5 files changed, 390 insertions(+), 20 deletions(-) create mode 100644 subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java create mode 100644 subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderUnitTest.java (limited to 'subprojects') 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 0de76519..c3f8a586 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 @@ -13,10 +13,7 @@ import tools.refinery.store.statecoding.ObjectCode; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple0; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Random; +import java.util.*; public abstract class AbstractNeighbourhoodCalculator { protected final List> nullImpactValues; @@ -56,13 +53,13 @@ public abstract class AbstractNeighbourhoodCalculator { } protected long getTupleHash1(Tuple tuple, Object value, ObjectCode objectCodeImpl) { - long result = value.hashCode(); + long result = Objects.hashCode(value); result = result * 31 + objectCodeImpl.get(tuple.get(0)); return result; } protected long getTupleHash2(Tuple tuple, Object value, ObjectCode objectCodeImpl) { - long result = value.hashCode(); + long result = Objects.hashCode(value); result = result * 31 + objectCodeImpl.get(tuple.get(0)); result = result * 31 + objectCodeImpl.get(tuple.get(1)); if (tuple.get(0) == tuple.get(1)) { @@ -72,7 +69,7 @@ public abstract class AbstractNeighbourhoodCalculator { } protected long getTupleHashN(Tuple tuple, Object value, ObjectCode objectCodeImpl) { - long result = value.hashCode(); + long result = Objects.hashCode(value); for (int i = 0; i < tuple.getSize(); i++) { result = result * 31 + objectCodeImpl.get(tuple.get(i)); } @@ -85,9 +82,9 @@ public abstract class AbstractNeighbourhoodCalculator { } protected long calculateModelCode(long lastSum) { - long result = 1; + long result = 0; for (var nullImpactValue : nullImpactValues) { - result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); + result = result * 31 + Objects.hashCode(nullImpactValue.get(Tuple0.INSTANCE)); } result += lastSum; return result; 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 08e3a90b..c4d86cf1 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 @@ -50,7 +50,8 @@ public class ObjectCodeImpl implements ObjectCode { public void set(int object, long value) { ensureSize(object); - vector[object]=value; + final long valueToPut = value == 0 ? 1 : value; + vector[object]=valueToPut; } public int getSize() { 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 e58a2502..34dba34e 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 @@ -29,11 +29,8 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { List> interpretations1, ObjectCode code1, List> interpretations2, - ObjectCode code2) { - if (code1.getSize() != code2.getSize()) { - return EquivalenceResult.DIFFERENT; - } - + ObjectCode code2) + { IntIntHashMap object2PermutationGroup = new IntIntHashMap(); List> permutationsGroups = new ArrayList<>(); @@ -69,12 +66,14 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { for (int o = 0; o < code.getSize(); o++) { if(! individuals.contains(o)){ long hash = code.get(o); - var equivalenceClass = result.get(hash); - if (equivalenceClass == null) { - equivalenceClass = new IntHashSet(); - result.put(hash, equivalenceClass); + if(hash != 0) { + var equivalenceClass = result.get(hash); + if (equivalenceClass == null) { + equivalenceClass = new IntHashSet(); + result.put(hash, equivalenceClass); + } + equivalenceClass.add(o); } - equivalenceClass.add(o); } } return result; diff --git a/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java b/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java new file mode 100644 index 00000000..8a9c0e9b --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java @@ -0,0 +1,178 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.map.Version; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class EquivalenceTest { + Symbol person = new Symbol<>("Person", 1, Boolean.class, false); + Symbol age = new Symbol<>("age", 1, Integer.class, null); + Symbol friend = new Symbol<>("friend", 2, Boolean.class, false); + Symbol parents = new Symbol<>("parents", 3, Boolean.class, false); + Symbol population = new Symbol<>("population", 0, Integer.class, 0); + + private ModelStore createStore() { + return ModelStore.builder() + .symbols(person, age, friend, parents, population) + .with(StateCoderAdapter.builder()) + .build(); + } + + @Test + void emptyModelCode0() { + ModelStore store = createStore(); + var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); + Model model = createStore().createEmptyModel(); + Version v1 = model.commit(); + Version v2 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v1, v2)); + + var personI = model.getInterpretation(person); + var friendI = model.getInterpretation(friend); + + personI.put(Tuple.of(1), true); + personI.put(Tuple.of(2), true); + friendI.put(Tuple.of(1, 2), true); + + Version v3 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v3)); + } + + @Test + void nullRelationTest() { + ModelStore store = createStore(); + var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); + Model model = createStore().createEmptyModel(); + + var populationI = model.getInterpretation(population); + + Version v1 = model.commit(); + + populationI.put(Tuple.of(), 1); + Version v2 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v2)); + + populationI.put(Tuple.of(), 2); + Version v3 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v2, v3)); + } + + @Test + void unaryBooleanTest() { + ModelStore store = createStore(); + var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); + Model model = createStore().createEmptyModel(); + + var personI = model.getInterpretation(person); + + Version v1 = model.commit(); + + personI.put(Tuple.of(1), true); + Version v2 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v2)); + + personI.put(Tuple.of(2), true); + Version v3 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v2, v3)); + + personI.put(Tuple.of(1), false); + Version v4 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v2, v4)); + } + + @Test + void unaryIntTest() { + ModelStore store = createStore(); + var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); + Model model = createStore().createEmptyModel(); + + var ageI = model.getInterpretation(age); + + ageI.put(Tuple.of(1), 3); + Version v1 = model.commit(); + + ageI.put(Tuple.of(1), 4); + Version v2 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v2)); + + ageI.put(Tuple.of(2), 4); + Version v3 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v2, v3)); + + ageI.put(Tuple.of(1), null); + Version v4 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v2, v4)); + } + + @Test + void binaryTest() { + ModelStore store = createStore(); + var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); + Model model = createStore().createEmptyModel(); + + var friendI = model.getInterpretation(friend); + + Version v1 = model.commit(); + + friendI.put(Tuple.of(1, 2), true); + Version v2 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v2)); + + friendI.put(Tuple.of(2, 1), true); + Version v3 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v2, v3)); + + friendI.put(Tuple.of(1, 2), false); + Version v4 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v2, v4)); + } + + @Test + void NaryTest() { + ModelStore store = createStore(); + var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); + Model model = createStore().createEmptyModel(); + + var parentsI = model.getInterpretation(parents); + + Version v1 = model.commit(); + + parentsI.put(Tuple.of(3, 1, 2), true); + Version v2 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v2)); + + parentsI.put(Tuple.of(4, 1, 2), true); + Version v3 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v2, v3)); + + parentsI.put(Tuple.of(3, 1, 2), false); + Version v4 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v2, v4)); + } +} diff --git a/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderUnitTest.java b/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderUnitTest.java new file mode 100644 index 00000000..d94df841 --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderUnitTest.java @@ -0,0 +1,195 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding; + +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; + +import java.util.Objects; + +import static org.junit.jupiter.api.Assertions.*; + +class StateCoderUnitTest { + Symbol person = new Symbol<>("Person", 1, Boolean.class, false); + Symbol age = new Symbol<>("age", 1, Integer.class, null); + Symbol friend = new Symbol<>("friend", 2, Boolean.class, false); + Symbol parents = new Symbol<>("parents", 3, Boolean.class, false); + Symbol population = new Symbol<>("population", 0, Integer.class, 0); + + private Model createEmptyModel() { + var store = ModelStore.builder() + .symbols(person, age, friend, parents, population) + .with(StateCoderAdapter.builder()) + .build(); + + return store.createEmptyModel(); + } + + @Test + void emptyModelCode0() { + Model model = createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + + assertEquals(0, stateCoder.calculateModelCode()); + + var personI = model.getInterpretation(person); + var friendI = model.getInterpretation(friend); + + personI.put(Tuple.of(1), true); + personI.put(Tuple.of(2), true); + friendI.put(Tuple.of(1, 2), true); + + assertNotEquals(0, stateCoder.calculateModelCode()); + } + + @Test + void emptyObjectCode0() { + Model model = createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + + var personI = model.getInterpretation(person); + var friendI = model.getInterpretation(friend); + + assertEquals(0, stateCoder.calculateObjectCode().get(1)); + assertEquals(0, stateCoder.calculateObjectCode().get(17)); + + personI.put(Tuple.of(1), true); + personI.put(Tuple.of(2), true); + friendI.put(Tuple.of(1, 2), true); + + assertNotEquals(0, stateCoder.calculateObjectCode().get(1)); + assertEquals(0, stateCoder.calculateObjectCode().get(17)); + } + + @Test + void nullRelationTest() { + Model model = createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + + var populationI = model.getInterpretation(population); + + final int hashOf0 = Objects.hashCode(0); + + assertEquals(hashOf0, stateCoder.calculateModelCode()); + + populationI.put(Tuple.of(), 1); + int code1 = stateCoder.calculateModelCode(); + + assertNotEquals(hashOf0, stateCoder.calculateModelCode()); + + populationI.put(Tuple.of(), 2); + int code2 = stateCoder.calculateModelCode(); + + assertNotEquals(code1, code2); + + populationI.put(Tuple.of(), 1); + assertEquals(code1, stateCoder.calculateModelCode()); + } + + @Test + void unaryBooleanTest() { + Model model = createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + + var personI = model.getInterpretation(person); + + assertEquals(0, stateCoder.calculateModelCode()); + + personI.put(Tuple.of(1), true); + int code1 = stateCoder.calculateModelCode(); + + assertNotEquals(0, stateCoder.calculateModelCode()); + + personI.put(Tuple.of(2), true); + int code2 = stateCoder.calculateModelCode(); + + assertNotEquals(code1, code2); + + personI.put(Tuple.of(1), false); + assertEquals(code1, stateCoder.calculateModelCode()); + } + + @Test + void unaryIntTest() { + Model model = createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + + var ageI = model.getInterpretation(age); + + assertEquals(0, stateCoder.calculateModelCode()); + + ageI.put(Tuple.of(1), 4); + int code0 = stateCoder.calculateModelCode(); + + assertNotEquals(0, code0); + + ageI.put(Tuple.of(1), 5); + int code1 = stateCoder.calculateModelCode(); + + assertNotEquals(code0, code1); + + ageI.put(Tuple.of(2), 5); + int code2 = stateCoder.calculateModelCode(); + + assertNotEquals(code1, code2); + + ageI.put(Tuple.of(1), null); + assertEquals(code1, stateCoder.calculateModelCode()); + } + + @Test + void binaryTest() { + Model model = createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + + var friendI = model.getInterpretation(friend); + + assertEquals(0, stateCoder.calculateModelCode()); + + friendI.put(Tuple.of(1, 2), true); + int code1 = stateCoder.calculateModelCode(); + + assertNotEquals(0, code1); + + friendI.put(Tuple.of(2, 1), true); + int code2 = stateCoder.calculateModelCode(); + + assertNotEquals(code1, code2); + + friendI.put(Tuple.of(1, 2), false); + int code3 = stateCoder.calculateModelCode(); + + assertEquals(code1, code3); + } + + @Test + void NaryTest() { + Model model = createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + + var parentsI = model.getInterpretation(parents); + + assertEquals(0, stateCoder.calculateModelCode()); + + parentsI.put(Tuple.of(3, 1, 2), true); + int code1 = stateCoder.calculateModelCode(); + + assertNotEquals(0, code1); + + parentsI.put(Tuple.of(4, 1, 2), true); + int code2 = stateCoder.calculateModelCode(); + + assertNotEquals(code1, code2); + + parentsI.put(Tuple.of(3, 1, 2), false); + int code3 = stateCoder.calculateModelCode(); + + assertEquals(code1, code3); + } +} -- cgit v1.2.3-54-g00ecf From 0c26310fc48b5359da11b32b04e008e64f91c10e Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Mon, 7 Aug 2023 16:23:57 +0200 Subject: Reduce complexity of Depth first search --- .../store/dse/strategy/DepthFirstStrategy.java | 89 +++++++++------------- 1 file changed, 36 insertions(+), 53 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java index 15529371..425e1e01 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java @@ -11,11 +11,8 @@ package tools.refinery.store.dse.strategy; import tools.refinery.store.dse.DesignSpaceExplorationAdapter; import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.internal.Activation; import tools.refinery.store.dse.objectives.Fitness; -import java.util.Collection; - public class DepthFirstStrategy implements Strategy { private DesignSpaceExplorationAdapter dseAdapter; @@ -60,69 +57,55 @@ public class DepthFirstStrategy implements Strategy { if (maxSolutions == 0) { return; } - mainloop: while (true) { - var globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); - if (!globalConstraintsAreSatisfied) { - var isSuccessfulUndo = dseAdapter.backtrack(); - if (!isSuccessfulUndo) { - // Global constraint is not satisfied and cannot backtrack. - break; - } - else { - // Global constraint is not satisfied, backtrack. - continue; - } + while (dseAdapter.getSolutions().size() < maxSolutions) { + if (!checkAndHandleGlobalConstraints()) { + // Global constraint is not satisfied and cannot backtrack. + return; } + // Global constraint is not satisfied, backtrack. Fitness fitness = dseAdapter.getFitness(); if (fitness.isSatisfiesHardObjectives()) { dseAdapter.newSolution(); - var solutions = dseAdapter.getSolutions().size(); - if (solutions >= maxSolutions) { + if (backTrackIfSolution && !dseAdapter.backtrack()) { + // Found a solution but cannot backtrack. return; } - if (backTrackIfSolution) { - var isSuccessfulUndo = dseAdapter.backtrack(); - if (!isSuccessfulUndo) { - // Found a solution but cannot backtrack. - break; - } else { - // Found a solution, backtrack. - continue; - } - } } - if (dseAdapter.getDepth() >= maxDepth) { - var isSuccessfulUndo = dseAdapter.backtrack(); - if (!isSuccessfulUndo) { - // Reached max depth but cannot backtrack. - break; - } + if (!checkAndHandleDepth()) { + // Reached max depth but cannot backtrack. + return; } - Collection activations; - do { - activations = dseAdapter.getUntraversedActivations(); - if (activations.isEmpty() && !dseAdapter.backtrack()) { - // No more transitions from current state and cannot backtrack. - break mainloop; - } - // No more transitions from current state, backtrack. - } while (activations.isEmpty()); + if (!backtrackToLastUntraversed()) { + return; + } dseAdapter.fireRandomActivation(); -// if (dseAdapter.isCurrentInTrajectory()) { -// if (!dseAdapter.backtrack()) { -//// TODO: throw exception -//// "The new state is present in the trajectory but cannot backtrack. Should never happen!" -// break; -// } -// else { -//// "The new state is already visited in the trajectory, backtrack." -// continue; -// } -// } } } + + private boolean checkAndHandleGlobalConstraints() { + // Global constraint is not satisfied and cannot backtrack. + return dseAdapter.checkGlobalConstraints() || dseAdapter.backtrack(); + // Global constraint is satisfied or backtrack. + } + + private boolean checkAndHandleDepth() { + // Reached max depth but cannot backtrack. + return dseAdapter.getDepth() < maxDepth || dseAdapter.backtrack(); + // Reached max depth or backtrack. + } + + private boolean backtrackToLastUntraversed() { + while (dseAdapter.getUntraversedActivations().isEmpty()) { + if (!dseAdapter.backtrack()) { + // No more transitions from current state and cannot backtrack. + return false; + } + // No more transitions from current state, backtrack. + } + return true; + } } -- cgit v1.2.3-54-g00ecf From f889dea154c57fd1831abc0db766f367665c0f60 Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Tue, 8 Aug 2023 13:20:07 +0200 Subject: Improve performance of best first earch --- .../tools/refinery/store/dse/objectives/Fitness.java | 1 + .../store/dse/strategy/BestFirstStrategy.java | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java index 0bf956d2..b1dc4442 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java @@ -32,6 +32,7 @@ public class Fitness extends HashMap { public boolean equals(Object other) { if (other == null) return false; if (getClass() != other.getClass()) return false; + if (!super.equals(other)) return false; return satisfiesHardObjectives == ((Fitness) other).satisfiesHardObjectives; } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java index 98af5695..0883d3d7 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java @@ -16,10 +16,7 @@ import tools.refinery.store.dse.internal.Activation; import tools.refinery.store.dse.objectives.Fitness; import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; -import java.util.PriorityQueue; +import java.util.*; public class BestFirstStrategy implements Strategy { @@ -37,6 +34,20 @@ public class BestFirstStrategy implements Strategy { public String toString() { return trajectory.toString() + fitness.toString(); } + + @Override + public int hashCode() { + return trajectory.get(trajectory.size() - 1).hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof TrajectoryWithFitness other) { + return Objects.equals(trajectory.get(trajectory.size() - 1), other.trajectory.get(other.trajectory.size() - 1)); +// return trajectory.equals(((TrajectoryWithFitness) obj).trajectory); + } + return false; + } } public BestFirstStrategy() { -- cgit v1.2.3-54-g00ecf From 1f853e4590d7f235bf8a63fa017fc92369a80a5a Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Tue, 8 Aug 2023 13:57:19 +0200 Subject: Refactor search strategy to improve readability --- .../java/tools/refinery/store/dse/Strategy.java | 2 +- .../DesignSpaceExplorationAdapterImpl.java | 10 +- .../store/dse/strategy/BestFirstStrategy.java | 29 ++-- .../store/dse/strategy/DepthFirstStrategy.java | 55 +++---- .../tools/refinery/store/dse/CRAExamplesTest.java | 4 +- .../java/tools/refinery/store/dse/DebugTest.java | 4 +- .../store/dse/DesignSpaceExplorationTest.java | 168 +++++++++++++++++++-- .../refinery/store/dse/TransformationRuleTest.java | 13 +- 8 files changed, 201 insertions(+), 84 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java index 409fe8a6..c60a4410 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java @@ -7,7 +7,7 @@ package tools.refinery.store.dse; public interface Strategy { - void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter); + void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter); void explore(); } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java index 008b2dab..04bba885 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -52,10 +52,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration private final Map fitnessCache = new HashMap<>(); - public List getTrajectory() { - return new ArrayList<>(trajectory); - } - public DesignSpaceExplorationAdapterImpl(Model model, DesignSpaceExplorationStoreAdapterImpl storeAdapter) { this.model = model; this.storeAdapter = storeAdapter; @@ -75,11 +71,16 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration objectives = storeAdapter.getObjectives(); statesAndTraversedActivations = new HashMap<>(); strategy = storeAdapter.getStrategy(); + strategy.initialize(this); modelVisualizerAdapter = model.tryGetAdapter(ModelVisualizerAdapter.class).orElse(null); isVisualizationEnabled = modelVisualizerAdapter != null; } + public List getTrajectory() { + return new ArrayList<>(trajectory); + } + @Override public Model getModel() { return model; @@ -94,7 +95,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration public List explore() { var state = model.commit(); trajectory.add(state); - strategy.initStrategy(this); strategy.explore(); if (isVisualizationEnabled) { modelVisualizerAdapter.visualize(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java index 0883d3d7..047b204a 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java @@ -22,8 +22,8 @@ public class BestFirstStrategy implements Strategy { private DesignSpaceExplorationAdapter dseAdapter; - private final int maxDepth; - private final int maxSolutions; + private int maxDepth = Integer.MAX_VALUE; + private int maxSolutions = Integer.MAX_VALUE; private boolean backTrackIfSolution = true; private boolean onlyBetterFirst = false; @@ -50,25 +50,18 @@ public class BestFirstStrategy implements Strategy { } } - public BestFirstStrategy() { - this(-1); - } - - public BestFirstStrategy(int maxDepth) { - this(maxDepth, -1); - } - - public BestFirstStrategy(int maxDepth, int maxSolutions) { - if (maxDepth < 0) { - this.maxDepth = Integer.MAX_VALUE; - } else { + public BestFirstStrategy withDepthLimit(int maxDepth) { + if (maxDepth >= 0) { this.maxDepth = maxDepth; } - if (maxSolutions < 0) { - this.maxSolutions = Integer.MAX_VALUE; - } else { + return this; + } + + public BestFirstStrategy withSolutionLimit(int maxSolutions) { + if (maxSolutions >= 0) { this.maxSolutions = maxSolutions; } + return this; } public BestFirstStrategy continueIfHardObjectivesFulfilled() { @@ -82,7 +75,7 @@ public class BestFirstStrategy implements Strategy { } @Override - public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { + public void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { this.dseAdapter = designSpaceExplorationAdapter; final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java index 425e1e01..5f7f61b8 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java @@ -1,12 +1,8 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors * * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ + */ package tools.refinery.store.dse.strategy; import tools.refinery.store.dse.DesignSpaceExplorationAdapter; @@ -17,38 +13,31 @@ public class DepthFirstStrategy implements Strategy { private DesignSpaceExplorationAdapter dseAdapter; - private int maxDepth; - private int maxSolutions; - private boolean backTrackIfSolution = true; + private int maxDepth = Integer.MAX_VALUE; + private int maxSolutions = Integer.MAX_VALUE; + private boolean backtrackFromSolution = true; - public DepthFirstStrategy() { - this(-1); - } - - public DepthFirstStrategy(int maxDepth) { - this(maxDepth, -1); - } - - public DepthFirstStrategy(int maxDepth, int maxSolutions) { - if (maxDepth < 0) { - this.maxDepth = Integer.MAX_VALUE; - } else { + public DepthFirstStrategy withDepthLimit(int maxDepth) { + if (maxDepth >= 0) { this.maxDepth = maxDepth; } - if (maxSolutions < 0) { - this.maxSolutions = Integer.MAX_VALUE; - } else { + return this; + } + + public DepthFirstStrategy withSolutionLimit(int maxSolutions) { + if (maxSolutions >= 0) { this.maxSolutions = maxSolutions; } + return this; } public DepthFirstStrategy continueIfHardObjectivesFulfilled() { - backTrackIfSolution = false; + backtrackFromSolution = false; return this; } @Override - public void initStrategy(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { + public void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { this.dseAdapter = designSpaceExplorationAdapter; } @@ -59,22 +48,18 @@ public class DepthFirstStrategy implements Strategy { } while (dseAdapter.getSolutions().size() < maxSolutions) { if (!checkAndHandleGlobalConstraints()) { - // Global constraint is not satisfied and cannot backtrack. return; } - // Global constraint is not satisfied, backtrack. Fitness fitness = dseAdapter.getFitness(); if (fitness.isSatisfiesHardObjectives()) { dseAdapter.newSolution(); - if (backTrackIfSolution && !dseAdapter.backtrack()) { - // Found a solution but cannot backtrack. + if (backtrackFromSolution && !dseAdapter.backtrack()) { return; } } if (!checkAndHandleDepth()) { - // Reached max depth but cannot backtrack. return; } @@ -87,24 +72,18 @@ public class DepthFirstStrategy implements Strategy { } private boolean checkAndHandleGlobalConstraints() { - // Global constraint is not satisfied and cannot backtrack. return dseAdapter.checkGlobalConstraints() || dseAdapter.backtrack(); - // Global constraint is satisfied or backtrack. } private boolean checkAndHandleDepth() { - // Reached max depth but cannot backtrack. return dseAdapter.getDepth() < maxDepth || dseAdapter.backtrack(); - // Reached max depth or backtrack. } private boolean backtrackToLastUntraversed() { while (dseAdapter.getUntraversedActivations().isEmpty()) { if (!dseAdapter.backtrack()) { - // No more transitions from current state and cannot backtrack. return false; } - // No more transitions from current state, backtrack. } return true; } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index d761db02..225de32e 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -201,8 +201,8 @@ class CRAExamplesTest { .with(DesignSpaceExplorationAdapter.builder() .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) .objectives(new AlwaysSatisfiedRandomHardObjective()) -// .strategy(new DepthFirstStrategy(3).continueIfHardObjectivesFulfilled() - .strategy(new BestFirstStrategy(6).continueIfHardObjectivesFulfilled() +// .strategy(new DepthFirstStrategy().withDepthLimit(3).continueIfHardObjectivesFulfilled() + .strategy(new BestFirstStrategy().withDepthLimit(6).continueIfHardObjectivesFulfilled() // .goOnOnlyIfFitnessIsBetter() )) .build(); diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index 36bc6cce..c6da896c 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -103,8 +103,8 @@ class DebugTest { .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule, createFeatureRule) .objectives(new AlwaysSatisfiedRandomHardObjective()) - .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() -// .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() + .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() +// .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() // .goOnOnlyIfFitnessIsBetter() )) .build(); diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java index 89adbf65..91e33f4a 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java @@ -49,7 +49,8 @@ class DesignSpaceExplorationTest { void createObjectTest() { var store = ModelStore.builder() .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .strategy(new DepthFirstStrategy().withDepthLimit(0))) .build(); var model = store.createEmptyModel(); @@ -77,7 +78,8 @@ class DesignSpaceExplorationTest { void deleteMiddleObjectTest() { var store = ModelStore.builder() .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .strategy(new DepthFirstStrategy())) .build(); var model = store.createEmptyModel(); @@ -118,7 +120,7 @@ class DesignSpaceExplorationTest { .symbols(classModel) .with(ViatraModelQueryAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy(0))) + .strategy(new DepthFirstStrategy().withDepthLimit(0))) .build(); var model = store.createEmptyModel(); @@ -158,7 +160,7 @@ class DesignSpaceExplorationTest { .queries(createClassPrecondition)) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule) - .strategy(new DepthFirstStrategy(4) + .strategy(new DepthFirstStrategy().withDepthLimit(0) )) .build(); @@ -204,7 +206,7 @@ class DesignSpaceExplorationTest { .queries(createClassPrecondition)) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule) - .strategy(new DepthFirstStrategy(4).continueIfHardObjectivesFulfilled() + .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() )) .build(); @@ -272,7 +274,7 @@ class DesignSpaceExplorationTest { .queries(createClassPrecondition, createFeaturePrecondition)) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule, createFeatureRule) - .strategy(new DepthFirstStrategy(10).continueIfHardObjectivesFulfilled() + .strategy(new DepthFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled() )) .build(); @@ -289,12 +291,81 @@ class DesignSpaceExplorationTest { } @Test - void BFSTrivialTest() { + void DFSSolutionLimitTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createFeatureRule = new TransformationRule("CreateFeature", + createFeaturePrecondition, + (model) -> { + var featuresInterpretation = model.getInterpretation(features); + var featureInterpretation = model.getInterpretation(feature); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + featureInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition, createFeaturePrecondition)) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule, createFeatureRule) + .strategy(new DepthFirstStrategy().withSolutionLimit(222) + .continueIfHardObjectivesFulfilled() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + assertEquals(222, states.size()); + } + + @Test + void BeFSTrivialTest() { var store = ModelStore.builder() .symbols(classModel) .with(ViatraModelQueryAdapter.builder()) .with(DesignSpaceExplorationAdapter.builder() - .strategy(new BestFirstStrategy(0))) + .strategy(new BestFirstStrategy().withDepthLimit(0))) .build(); var model = store.createEmptyModel(); @@ -305,7 +376,7 @@ class DesignSpaceExplorationTest { } @Test - void BFSOneRuleTest() { + void BeFSOneRuleTest() { var createClassPrecondition = Query.of("CreateClassPrecondition", (builder, model) -> builder.clause( classModelView.call(model) @@ -334,7 +405,7 @@ class DesignSpaceExplorationTest { .queries(createClassPrecondition)) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule) - .strategy(new BestFirstStrategy(4) + .strategy(new BestFirstStrategy().withDepthLimit(4) )) .build(); @@ -351,7 +422,7 @@ class DesignSpaceExplorationTest { } @Test - void BFSContinueTest() { + void BeFSContinueTest() { var createClassPrecondition = Query.of("CreateClassPrecondition", (builder, model) -> builder.clause( classModelView.call(model) @@ -380,7 +451,7 @@ class DesignSpaceExplorationTest { .queries(createClassPrecondition)) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule) - .strategy(new BestFirstStrategy(4).continueIfHardObjectivesFulfilled() + .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() )) .build(); @@ -397,7 +468,7 @@ class DesignSpaceExplorationTest { } @Test - void BFSCompletenessTest() { + void BeFSCompletenessTest() { var createClassPrecondition = Query.of("CreateClassPrecondition", (builder, model) -> builder.clause( classModelView.call(model) @@ -448,7 +519,7 @@ class DesignSpaceExplorationTest { .queries(createClassPrecondition, createFeaturePrecondition)) .with(DesignSpaceExplorationAdapter.builder() .transformations(createClassRule, createFeatureRule) - .strategy(new BestFirstStrategy(10).continueIfHardObjectivesFulfilled() + .strategy(new BestFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled() )) .build(); @@ -464,4 +535,73 @@ class DesignSpaceExplorationTest { assertEquals(2047, states.size()); } + @Test + void BeFSSolutionLimitTest() { + var createClassPrecondition = Query.of("CreateClassPrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createClassRule = new TransformationRule("CreateClass", + createClassPrecondition, + (model) -> { + var classesInterpretation = model.getInterpretation(classes); + var classElementInterpretation = model.getInterpretation(classElement); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + classElementInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", + (builder, model) -> builder.clause( + classModelView.call(model) + )); + + var createFeatureRule = new TransformationRule("CreateFeature", + createFeaturePrecondition, + (model) -> { + var featuresInterpretation = model.getInterpretation(features); + var featureInterpretation = model.getInterpretation(feature); + return ((Tuple activation) -> { + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var modelElement = activation.get(0); + + var newClassElement = dseAdapter.createObject(); + var newClassElementId = newClassElement.get(0); + + featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); + featureInterpretation.put(Tuple.of(newClassElementId), true); + }); + }); + + var store = ModelStore.builder() + .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) + .with(ViatraModelQueryAdapter.builder() + .queries(createClassPrecondition, createFeaturePrecondition)) + .with(DesignSpaceExplorationAdapter.builder() + .transformations(createClassRule, createFeatureRule) + .strategy(new BestFirstStrategy().withSolutionLimit(222) + .continueIfHardObjectivesFulfilled() + )) + .build(); + + var model = store.createEmptyModel(); + var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + + var modelElementInterpretation = model.getInterpretation(classModel); + modelElementInterpretation.put(dseAdapter.createObject(), true); + queryEngine.flushChanges(); + + var states = dseAdapter.explore(); + assertEquals(222, states.size()); + } + } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java index 3aa4c92e..5d24d712 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java @@ -6,6 +6,7 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; +import tools.refinery.store.dse.strategy.DepthFirstStrategy; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; @@ -103,7 +104,8 @@ class TransformationRuleTest { .with(ViatraModelQueryAdapter.builder() .queries(assignFeaturePrecondition, assignFeaturePreconditionHelper, deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .strategy(new DepthFirstStrategy().withDepthLimit(0))) .build(); var model = store.createEmptyModel(); @@ -195,7 +197,8 @@ class TransformationRuleTest { .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) .with(ViatraModelQueryAdapter.builder() .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .strategy(new DepthFirstStrategy().withDepthLimit(0))) .build(); var model = store.createEmptyModel(); @@ -276,7 +279,8 @@ class TransformationRuleTest { .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) .with(ViatraModelQueryAdapter.builder() .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .strategy(new DepthFirstStrategy().withDepthLimit(0))) .build(); var model = store.createEmptyModel(); @@ -352,7 +356,8 @@ class TransformationRuleTest { .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) .with(ViatraModelQueryAdapter.builder() .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder()) + .with(DesignSpaceExplorationAdapter.builder() + .strategy(new DepthFirstStrategy().withDepthLimit(0))) .build(); var model = store.createEmptyModel(); -- cgit v1.2.3-54-g00ecf From dddea012f920f922a94157c740d99a3b5a474f6e Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 8 Aug 2023 17:52:58 +0200 Subject: Non-lazy NeighbourhoodCalculator for more accurate StateCoderBuilderImpl. --- .../internal/StateCoderBuilderImpl.java | 9 +- .../AbstractNeighbourhoodCalculator.java | 19 +-- .../CollectionNeighbourhoodCalculator.java | 131 --------------------- .../neighbourhood/LazyNeighbourhoodCalculator.java | 31 +++-- .../LazyNeighbourhoodCalculatorFactory.java | 20 ---- .../neighbourhood/NeighbourhoodCalculator.java | 112 ++++++++++++++++++ .../statecoding/neighbourhood/ObjectCodeImpl.java | 11 +- 7 files changed, 152 insertions(+), 181 deletions(-) delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/CollectionNeighbourhoodCalculator.java delete mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculatorFactory.java create mode 100644 subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java (limited to 'subprojects') 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 8268a826..05b47c52 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 @@ -10,11 +10,8 @@ import tools.refinery.store.model.ModelStore; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.representation.AnySymbol; import tools.refinery.store.representation.Symbol; -import tools.refinery.store.statecoding.StateCodeCalculatorFactory; -import tools.refinery.store.statecoding.StateCoderBuilder; -import tools.refinery.store.statecoding.StateCoderStoreAdapter; -import tools.refinery.store.statecoding.StateEquivalenceChecker; -import tools.refinery.store.statecoding.neighbourhood.LazyNeighbourhoodCalculatorFactory; +import tools.refinery.store.statecoding.*; +import tools.refinery.store.statecoding.neighbourhood.NeighbourhoodCalculator; import tools.refinery.store.statecoding.stateequivalence.StateEquivalenceCheckerImpl; import tools.refinery.store.tuple.Tuple1; @@ -24,7 +21,7 @@ public class StateCoderBuilderImpl implements StateCoderBuilder { Set excluded = new HashSet<>(); IntHashSet individuals = new IntHashSet(); - StateCodeCalculatorFactory calculator = new LazyNeighbourhoodCalculatorFactory(); + StateCodeCalculatorFactory calculator = NeighbourhoodCalculator::new; StateEquivalenceChecker checker = new StateEquivalenceCheckerImpl(); @Override 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 c3f8a586..8fcf24b1 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 @@ -7,7 +7,6 @@ package tools.refinery.store.statecoding.neighbourhood; import org.eclipse.collections.api.set.primitive.IntSet; import org.eclipse.collections.impl.map.mutable.primitive.IntLongHashMap; -import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; import tools.refinery.store.model.Interpretation; import tools.refinery.store.statecoding.ObjectCode; import tools.refinery.store.tuple.Tuple; @@ -20,6 +19,8 @@ public abstract class AbstractNeighbourhoodCalculator { protected final LinkedHashMap, long[]> impactValues; protected final IntLongHashMap individualHashValues; + protected static final long PRIME = 31; + protected AbstractNeighbourhoodCalculator(List> interpretations, IntSet individuals) { this.nullImpactValues = new ArrayList<>(); this.impactValues = new LinkedHashMap<>(); @@ -45,25 +46,25 @@ public abstract class AbstractNeighbourhoodCalculator { } } - protected void initializeWithIndividuals(ObjectCodeImpl previous, LongIntHashMap hash2Amount) { + protected void initializeWithIndividuals(ObjectCodeImpl previous) { for (var entry : individualHashValues.keyValuesView()) { previous.set(entry.getOne(), entry.getTwo()); - hash2Amount.put(entry.getTwo(), 1); } } protected long getTupleHash1(Tuple tuple, Object value, ObjectCode objectCodeImpl) { long result = Objects.hashCode(value); - result = result * 31 + objectCodeImpl.get(tuple.get(0)); + result = result * PRIME + objectCodeImpl.get(tuple.get(0)); return result; } protected long getTupleHash2(Tuple tuple, Object value, ObjectCode objectCodeImpl) { long result = Objects.hashCode(value); - result = result * 31 + objectCodeImpl.get(tuple.get(0)); - result = result * 31 + objectCodeImpl.get(tuple.get(1)); + result = result * PRIME + objectCodeImpl.get(tuple.get(0)); + result = result * PRIME + objectCodeImpl.get(tuple.get(1)); if (tuple.get(0) == tuple.get(1)) { - result *= 31; + result += PRIME; + result *= PRIME; } return result; } @@ -71,7 +72,7 @@ public abstract class AbstractNeighbourhoodCalculator { protected long getTupleHashN(Tuple tuple, Object value, ObjectCode objectCodeImpl) { long result = Objects.hashCode(value); for (int i = 0; i < tuple.getSize(); i++) { - result = result * 31 + objectCodeImpl.get(tuple.get(i)); + result = result * PRIME + objectCodeImpl.get(tuple.get(i)); } return result; } @@ -84,7 +85,7 @@ public abstract class AbstractNeighbourhoodCalculator { protected long calculateModelCode(long lastSum) { long result = 0; for (var nullImpactValue : nullImpactValues) { - result = result * 31 + Objects.hashCode(nullImpactValue.get(Tuple0.INSTANCE)); + result = result * PRIME + Objects.hashCode(nullImpactValue.get(Tuple0.INSTANCE)); } result += lastSum; return result; diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/CollectionNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/CollectionNeighbourhoodCalculator.java deleted file mode 100644 index 058750ee..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/CollectionNeighbourhoodCalculator.java +++ /dev/null @@ -1,131 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.statecoding.neighbourhood; - -import org.eclipse.collections.api.set.primitive.MutableLongSet; -import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; -import tools.refinery.store.model.Interpretation; -import tools.refinery.store.statecoding.StateCodeCalculator; -import tools.refinery.store.statecoding.StateCoderResult; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.Tuple0; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Random; - -public class CollectionNeighbourhoodCalculator implements StateCodeCalculator { - protected final List> nullImpactValues; - protected final LinkedHashMap, long[]> impactValues; - - public CollectionNeighbourhoodCalculator(List> interpretations) { - this.nullImpactValues = new ArrayList<>(); - this.impactValues = new LinkedHashMap<>(); - Random random = new Random(1); - - for (Interpretation interpretation : interpretations) { - int arity = interpretation.getSymbol().arity(); - if (arity == 0) { - nullImpactValues.add(interpretation); - } else { - long[] impact = new long[arity]; - for (int i = 0; i < arity; i++) { - impact[i] = random.nextLong(); - } - impactValues.put(interpretation, impact); - } - } - } - - @Override - public StateCoderResult calculateCodes() { - ObjectCodeImpl previous = new ObjectCodeImpl(); - ObjectCodeImpl next = new ObjectCodeImpl(); - - int previousSize = 1; - long lastSum; - boolean grows; - - do{ - for (var impactValueEntry : this.impactValues.entrySet()) { - Interpretation interpretation = impactValueEntry.getKey(); - long[] impact = impactValueEntry.getValue(); - var cursor = interpretation.getAll(); - while (cursor.move()) { - Tuple tuple = cursor.getKey(); - Object value = cursor.getValue(); - long tupleHash = getTupleHash(tuple, value, previous); - addHash(next, tuple, impact, tupleHash); - } - } - - previous = next; - next = null; - lastSum = 0; - MutableLongSet codes = new LongHashSet(); - for (int i = 0; i < previous.getSize(); i++) { - long objectHash = previous.get(i); - codes.add(objectHash); - - final long shifted1 = objectHash>>> 32; - final long shifted2 = objectHash << 32; - lastSum += shifted1 + shifted2; - } - int nextSize = codes.size(); - grows = previousSize < nextSize; - previousSize = nextSize; - - if(grows) { - next = new ObjectCodeImpl(previous); - } - } while (grows); - - long result = 1; - for (var nullImpactValue : nullImpactValues) { - result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); - } - result += lastSum; - - return new StateCoderResult((int) result, previous); - } - - protected long getTupleHash(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { - long result = value.hashCode(); - int arity = tuple.getSize(); - if (arity == 1) { - result = result * 31 + objectCodeImpl.get(tuple.get(0)); - } else if (arity == 2) { - result = result * 31 + objectCodeImpl.get(tuple.get(0)); - result = result * 31 + objectCodeImpl.get(tuple.get(1)); - if (tuple.get(0) == tuple.get(1)) { - result++; - } - } else if (arity > 2) { - for (int i = 0; i < arity; i++) { - result = result * 31 + objectCodeImpl.get(tuple.get(i)); - } - } - return result; - } - - protected void addHash(ObjectCodeImpl objectCodeImpl, Tuple tuple, long[] impact, long tupleHashCode) { - if (tuple.getSize() == 1) { - addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); - } else if (tuple.getSize() == 2) { - addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); - addHash(objectCodeImpl, tuple.get(1), impact[1], tupleHashCode); - } else if (tuple.getSize() > 2) { - for (int i = 0; i < tuple.getSize(); i++) { - addHash(objectCodeImpl, tuple.get(i), impact[i], tupleHashCode); - } - } - } - - protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { - objectCodeImpl.set(o, objectCodeImpl.get(o) + tupleHash * impact); - } -} 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 98a75e08..2ffbef5e 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 @@ -22,34 +22,41 @@ public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator } public StateCoderResult calculateCodes() { - ObjectCodeImpl previous = new ObjectCodeImpl(); - LongIntHashMap hash2Amount = new LongIntHashMap(); - - initializeWithIndividuals(previous, hash2Amount); + ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); + LongIntHashMap prevHash2Amount = new LongIntHashMap(); long lastSum; // All hash code is 0, except to the individuals. - int lastSize = hash2Amount.size() + 1; + int lastSize = 1; + boolean first = true; boolean grows; + int rounds = 0; do { - ObjectCodeImpl next = new ObjectCodeImpl(); - constructNextObjectCodes(previous, next, hash2Amount); + final ObjectCodeImpl nextObjectCode; + if (first) { + nextObjectCode = new ObjectCodeImpl(); + initializeWithIndividuals(nextObjectCode); + } else { + nextObjectCode = new ObjectCodeImpl(previousObjectCode); + } + constructNextObjectCodes(previousObjectCode, nextObjectCode, prevHash2Amount); LongIntHashMap nextHash2Amount = new LongIntHashMap(); - lastSum = calculateLastSum(previous, next, hash2Amount, nextHash2Amount); + lastSum = calculateLastSum(previousObjectCode, nextObjectCode, prevHash2Amount, nextHash2Amount); int nextSize = nextHash2Amount.size(); grows = nextSize > lastSize; lastSize = nextSize; + first = false; - previous = next; - hash2Amount = nextHash2Amount; - } while (grows); + previousObjectCode = nextObjectCode; + prevHash2Amount = nextHash2Amount; + } while (grows && rounds++ < 4/*&& lastSize < previousObjectCode.getSize()*/); long result = calculateModelCode(lastSum); - return new StateCoderResult((int) result, previous); + return new StateCoderResult((int) result, previousObjectCode); } private long calculateLastSum(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount, diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculatorFactory.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculatorFactory.java deleted file mode 100644 index 2e499f95..00000000 --- a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/LazyNeighbourhoodCalculatorFactory.java +++ /dev/null @@ -1,20 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.statecoding.neighbourhood; - -import org.eclipse.collections.api.set.primitive.IntSet; -import tools.refinery.store.model.Interpretation; -import tools.refinery.store.statecoding.StateCodeCalculator; -import tools.refinery.store.statecoding.StateCodeCalculatorFactory; - -import java.util.List; - -public class LazyNeighbourhoodCalculatorFactory implements StateCodeCalculatorFactory { - @Override - public StateCodeCalculator create(List> interpretations, IntSet individuals) { - return new LazyNeighbourhoodCalculator(interpretations,individuals); - } -} 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 new file mode 100644 index 00000000..d3b3ccae --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/NeighbourhoodCalculator.java @@ -0,0 +1,112 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.statecoding.neighbourhood; + +import org.eclipse.collections.api.set.primitive.IntSet; +import tools.refinery.store.map.Cursor; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.statecoding.ObjectCode; +import tools.refinery.store.statecoding.StateCodeCalculator; +import tools.refinery.store.statecoding.StateCoderResult; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple0; + +import java.util.List; +import java.util.Objects; + +public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { + public NeighbourhoodCalculator(List> interpretations, IntSet individuals) { + super(interpretations, individuals); + } + + public StateCoderResult calculateCodes() { + ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); + initializeWithIndividuals(previousObjectCode); + + int rounds = 0; + do { + final ObjectCodeImpl nextObjectCode = rounds == 0 ? new ObjectCodeImpl() : + new ObjectCodeImpl(previousObjectCode.getSize()); + + constructNextObjectCodes(previousObjectCode, nextObjectCode); + previousObjectCode = nextObjectCode; + rounds++; + } while (rounds <= 7 && rounds <= previousObjectCode.getSize()); + + long result = calculateLastSum(previousObjectCode); + return new StateCoderResult((int) result, previousObjectCode); + } + + private long calculateLastSum(ObjectCode codes) { + long result = 0; + for (var nullImpactValue : nullImpactValues) { + result = result * 31 + Objects.hashCode(nullImpactValue.get(Tuple0.INSTANCE)); + } + + for (int i = 0; i < codes.getSize(); i++) { + final long hash = codes.get(i); + result += hash*PRIME; + } + + return result; + } + + private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next) { + for (var impactValueEntry : this.impactValues.entrySet()) { + Interpretation interpretation = impactValueEntry.getKey(); + var cursor = interpretation.getAll(); + int arity = interpretation.getSymbol().arity(); + long[] impactValue = impactValueEntry.getValue(); + + if (arity == 1) { + while (cursor.move()) { + impactCalculation1(previous, next, impactValue, cursor); + } + } else if (arity == 2) { + while (cursor.move()) { + impactCalculation2(previous, next, impactValue, cursor); + } + } else { + while (cursor.move()) { + impactCalculationN(previous, next, impactValue, cursor); + } + } + } + } + + + private void impactCalculation1(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor cursor) { + + Tuple tuple = cursor.getKey(); + int o = tuple.get(0); + Object value = cursor.getValue(); + long tupleHash = getTupleHash1(tuple, value, previous); + addHash(next, o, impactValues[0], tupleHash); + } + + private void impactCalculation2(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor cursor) { + final Tuple tuple = cursor.getKey(); + final int o1 = tuple.get(0); + final int o2 = tuple.get(1); + + Object value = cursor.getValue(); + long tupleHash = getTupleHash2(tuple, value, previous); + + addHash(next, o1, impactValues[0], tupleHash); + addHash(next, o2, impactValues[1], tupleHash); + } + + private void impactCalculationN(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor cursor) { + final Tuple tuple = cursor.getKey(); + + Object value = cursor.getValue(); + long tupleHash = getTupleHashN(tuple, value, previous); + + for (int i = 0; i < tuple.getSize(); i++) { + addHash(next, tuple.get(i), impactValues[i], tupleHash); + } + } +} 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 c4d86cf1..0d629176 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 @@ -18,9 +18,14 @@ public class ObjectCodeImpl implements ObjectCode { size = 0; } - public ObjectCodeImpl(ObjectCodeImpl sameSize) { - this.vector = new long[sameSize.size]; - this.size = sameSize.size; + public ObjectCodeImpl(int size) { + this.vector = new long[size]; + this.size = size; + } + + public ObjectCodeImpl(ObjectCodeImpl copy) { + this.vector = Arrays.copyOf(copy.vector,copy.size); + this.size = copy.size; } public void ensureSize(int object) { -- cgit v1.2.3-54-g00ecf From 52c8e89faa49988bc47a15256ed818f3b78bb56b Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 8 Aug 2023 19:07:27 +0200 Subject: Test cases for Equivalence accuracy measurements --- .../store/statecoding/ExperimentalSetupTest.java | 189 +++++++++++++++------ 1 file changed, 140 insertions(+), 49 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/test/java/tools/refinery/store/statecoding/ExperimentalSetupTest.java b/subprojects/store/src/test/java/tools/refinery/store/statecoding/ExperimentalSetupTest.java index 87b1623c..25b5dca1 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/statecoding/ExperimentalSetupTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/ExperimentalSetupTest.java @@ -5,16 +5,52 @@ */ package tools.refinery.store.statecoding; +import org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap; +import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; import tools.refinery.store.map.Version; +import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; -import java.util.*; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + class ExperimentalSetupTest { - public static void generate(int size) { + static class ExperimentalSetupResult { + int versions = 0; + int different = 0; + int isomorphic = 0; + int unknown = 0; + + double failureRatio() { + return (different + 0.0) / versions; + } + + @Override + public String toString() { + return "ExperimentalSetupResult{" + + "versions=" + versions + + ", different=" + different + + ", isomorphic=" + isomorphic + + ", unknown=" + unknown + + ", ratio= " + failureRatio() + + '}'; + } + } + + static int MAX = 100000; + + public static ExperimentalSetupResult generate(int size, boolean permuteTypes) { Symbol person = new Symbol<>("Person", 1, Boolean.class, false); Symbol friend = new Symbol<>("friend", 2, Boolean.class, false); @@ -25,13 +61,13 @@ class ExperimentalSetupTest { .build(); Set versions = new HashSet<>(); - Map> codes = new HashMap<>(); + IntObjectHashMap> codes = new IntObjectHashMap<>(); var empty = store.createEmptyModel(); - var pI = empty.getInterpretation(person); - - for (int i = 0; i < size; i++) { - pI.put(Tuple.of(i), true); + if (!permuteTypes) { + for (int i = 0; i < size; i++) { + empty.getInterpretation(person).put(Tuple.of(i), true); + } } var emptyVersion = empty.commit(); @@ -42,12 +78,27 @@ class ExperimentalSetupTest { codes.put(emptyCode, emptyList); var storeAdapter = store.getAdapter(StateCoderStoreAdapter.class); + var result = new ExperimentalSetupResult(); - int dif = 0; - int iso = 0; - int unk = 0; + int steps = 0; - //int step = 0 + if (permuteTypes) { + for (int i = 0; i < size; i++) { + var previousVersions = new HashSet<>(versions); + for (var version : previousVersions) { + var model = store.createModelForState(version); + model.getInterpretation(person).put(Tuple.of(i), true); + + saveAsNewVersion(versions, codes, storeAdapter, result, model); + + logProgress(steps++); + if (steps > MAX) { + result.versions = versions.size(); + return result; + } + } + } + } for (int i = 0; i < size; i++) { for (int j = 0; j < size; j++) { @@ -57,51 +108,91 @@ class ExperimentalSetupTest { var model = store.createModelForState(version); model.getInterpretation(friend).put(Tuple.of(i, j), true); - Version version1 = model.commit(); - var stateCode = model.getAdapter(StateCoderAdapter.class).calculateStateCode(); - int code = stateCode.modelCode(); - //System.out.println(step+++" ->" +code); - if (codes.containsKey(code)) { - Version similar = codes.get(code).get(0); - - var outcome = storeAdapter.checkEquivalence(version1, similar); - if (outcome == StateEquivalenceChecker.EquivalenceResult.DIFFERENT) { - System.out.println(); - var c = model.getInterpretation(friend).getAll(); - while (c.move()) { - System.out.println(c.getKey().toString()); - } - System.out.println("vs"); - var c2 = store.createModelForState(similar).getInterpretation(friend).getAll(); - while (c2.move()) { - System.out.println(c2.getKey().toString()); - } - - dif++; - } else if (outcome == StateEquivalenceChecker.EquivalenceResult.UNKNOWN) { - unk++; - } else { - iso++; - } - } else { - versions.add(version1); - - List newList = new ArrayList<>(); - newList.add(version1); - codes.put(code, newList); + saveAsNewVersion(versions, codes, storeAdapter, result, model); + + logProgress(steps++); + if (steps > MAX) { + result.versions = versions.size(); + return result; } } } } - System.out.printf("v=%d i=%d d=%d u=%d\n", versions.size(), iso, dif, unk); + result.versions = versions.size(); + return result; } - @Test - void runTests() { - for (int i = 0; i < 5; i++) { - System.out.println("size = " + i); - generate(i); + private static void saveAsNewVersion(Set versions, IntObjectHashMap> codes, + StateCoderStoreAdapter storeAdapter, ExperimentalSetupResult result, Model model) { + Version version1 = model.commit(); + + var stateCode = model.getAdapter(StateCoderAdapter.class).calculateStateCode(); + int code = stateCode.modelCode(); + if (codes.containsKey(code)) { + Version similar = codes.get(code).get(0); + + var outcome = storeAdapter.checkEquivalence(version1, similar); + if (outcome == StateEquivalenceChecker.EquivalenceResult.DIFFERENT) { + result.different++; + } else if (outcome == StateEquivalenceChecker.EquivalenceResult.UNKNOWN) { + result.unknown++; + } else { + result.isomorphic++; + } + } else { + versions.add(version1); + + List newList = new ArrayList<>(); + newList.add(version1); + codes.put(code, newList); + } + } + + private static void logProgress(int steps) { + if (steps % 10000 == 0) { + System.out.println("Steps: " + steps + " / " + MAX); } } + + static final double limit = 0.01; + + @Test + void test0() { + assertEquals(1, generate(0, true).versions); + } + + @ParameterizedTest + @ValueSource(ints = {1, 2, 3, 4}) + void testForSmallUntypedModels(int size) { + var res = generate(size, false); + System.out.println(res); + assertTrue(res.failureRatio() < limit); + } + + @ParameterizedTest + @ValueSource(ints = {1, 2, 3}) + void testForSmallTypedModels(int size) { + var res = generate(size, true); + System.out.println(res); + assertTrue(res.failureRatio() < limit); + } + + @Test + @Tag("fuzz") + @Tag("slow") + void testForLargeTypedModels() { + var res = generate(10, true); + System.out.println(res); + assertTrue(res.failureRatio() < limit); + } + + @Test + @Tag("fuzz") + @Tag("slow") + void testForLargeUntypedModels() { + var res = generate(10, false); + System.out.println(res); + assertTrue(res.failureRatio() < limit); + } } -- cgit v1.2.3-54-g00ecf From db7a2892def64f0d155d4fc9525be11a1833f8f8 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 8 Aug 2023 19:18:45 +0200 Subject: Added two build tests for custom coding and equivalence checking algorithms. --- .../store/statecoding/StateCoderBuildTest.java | 74 ++++++++++++++++++---- 1 file changed, 60 insertions(+), 14 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java b/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java index a4e953ea..0b738005 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/StateCoderBuildTest.java @@ -58,14 +58,14 @@ class StateCoderBuildTest { int code = stateCoder.calculateStateCode().modelCode(); - ageI.put(Tuple.of(1),3); - assertEquals(code,stateCoder.calculateStateCode().modelCode()); + ageI.put(Tuple.of(1), 3); + assertEquals(code, stateCoder.calculateStateCode().modelCode()); - ageI.put(Tuple.of(1),null); - assertEquals(code,stateCoder.calculateStateCode().modelCode()); + ageI.put(Tuple.of(1), null); + assertEquals(code, stateCoder.calculateStateCode().modelCode()); - personI.put(Tuple.of(2),false); - assertEquals(code,stateCoder.calculateStateCode().modelCode()); + personI.put(Tuple.of(2), false); + assertEquals(code, stateCoder.calculateStateCode().modelCode()); } @Test @@ -80,14 +80,14 @@ class StateCoderBuildTest { var friendI = model.getInterpretation(friend); - friendI.put(Tuple.of(1,2),true); + friendI.put(Tuple.of(1, 2), true); int code1 = stateCoder.calculateModelCode(); - friendI.put(Tuple.of(1,2),false); - friendI.put(Tuple.of(2,1),true); + friendI.put(Tuple.of(1, 2), false); + friendI.put(Tuple.of(2, 1), true); int code2 = stateCoder.calculateModelCode(); - assertEquals(code1,code2); + assertEquals(code1, code2); } @Test @@ -103,16 +103,62 @@ class StateCoderBuildTest { var friendI = model.getInterpretation(friend); - friendI.put(Tuple.of(1,2),true); + friendI.put(Tuple.of(1, 2), true); int code1 = stateCoder.calculateModelCode(); - friendI.put(Tuple.of(1,2),false); - friendI.put(Tuple.of(2,1),true); + friendI.put(Tuple.of(1, 2), false); + friendI.put(Tuple.of(2, 1), true); int code2 = stateCoder.calculateModelCode(); - assertNotEquals(code1,code2); + assertNotEquals(code1, code2); } + @Test + void customStateCoderTest() { + final boolean[] called = new boolean[]{false}; + StateCodeCalculator mock = () -> { + called[0] = true; + return null; + }; + + var store = ModelStore.builder() + .symbols(friend) + .with(StateCoderAdapter.builder() + .stateCodeCalculatorFactory((interpretations, individuals) -> mock)) + .build(); + + var model = store.createEmptyModel(); + var stateCoder = model.getAdapter(StateCoderAdapter.class); + + stateCoder.calculateStateCode(); + + assertTrue(called[0]); + } + + @Test + void customEquivalenceCheckerTest() { + final boolean[] called = new boolean[]{false}; + StateEquivalenceChecker mock = (p1, p2, p3, p4, p5) -> { + called[0] = true; + return StateEquivalenceChecker.EquivalenceResult.UNKNOWN; + }; + + var store = ModelStore.builder() + .symbols(friend) + .with(StateCoderAdapter.builder() + .stateEquivalenceChecker(mock)) + .build(); + + var model = store.createEmptyModel(); + var v1 = model.commit(); + var v2 = model.commit(); + + store.getAdapter(StateCoderStoreAdapter.class).checkEquivalence(v1, v2); + + assertTrue(called[0]); + } + + private static void fill(Interpretation personI, Interpretation friendI, Interpretation ageI) { personI.put(Tuple.of(1), true); personI.put(Tuple.of(2), true); -- cgit v1.2.3-54-g00ecf From 7fb2e47c13f798511b0fa6b47c34f53d42c00f34 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 8 Aug 2023 19:37:54 +0200 Subject: ObjectCodeImpl.effectiveSize introduced to correctly handle models with different indices. --- .../neighbourhood/NeighbourhoodCalculator.java | 2 +- .../statecoding/neighbourhood/ObjectCodeImpl.java | 29 ++++++++++++++-------- 2 files changed, 20 insertions(+), 11 deletions(-) (limited to 'subprojects') 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 d3b3ccae..5b3e5ea3 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 @@ -34,7 +34,7 @@ public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator imp constructNextObjectCodes(previousObjectCode, nextObjectCode); previousObjectCode = nextObjectCode; rounds++; - } while (rounds <= 7 && rounds <= previousObjectCode.getSize()); + } while (rounds <= 7 && rounds <= previousObjectCode.getEffectiveSize()); long result = calculateLastSum(previousObjectCode); return new StateCoderResult((int) result, previousObjectCode); 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 0d629176..0cd7ff58 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 @@ -12,31 +12,35 @@ import java.util.Arrays; public class ObjectCodeImpl implements ObjectCode { private long[] vector; private int size; + private int effectiveSize; public ObjectCodeImpl() { vector = new long[10]; size = 0; + effectiveSize = 0; } public ObjectCodeImpl(int size) { this.vector = new long[size]; this.size = size; + effectiveSize = 0; } public ObjectCodeImpl(ObjectCodeImpl copy) { - this.vector = Arrays.copyOf(copy.vector,copy.size); + this.vector = Arrays.copyOf(copy.vector, copy.size); this.size = copy.size; + effectiveSize = copy.effectiveSize; } public void ensureSize(int object) { - if(object >= size) { - size = object+1; + if (object >= size) { + size = object + 1; } - if(object >= vector.length) { - int newLength = vector.length*2; - while(object >= newLength) { - newLength*=2; + if (object >= vector.length) { + int newLength = vector.length * 2; + while (object >= newLength) { + newLength *= 2; } long[] newVector = new long[newLength]; @@ -46,7 +50,7 @@ public class ObjectCodeImpl implements ObjectCode { } public long get(int object) { - if(object < vector.length) { + if (object < vector.length) { return vector[object]; } else { return 0; @@ -56,17 +60,22 @@ public class ObjectCodeImpl implements ObjectCode { public void set(int object, long value) { ensureSize(object); final long valueToPut = value == 0 ? 1 : value; - vector[object]=valueToPut; + if (vector[object] == 0) effectiveSize++; + vector[object] = valueToPut; } public int getSize() { return this.size; } + public int getEffectiveSize() { + return this.effectiveSize; + } + @Override public String toString() { return "ObjectCodeImpl{" + - "vector=" + Arrays.toString(Arrays.copyOf(vector,this.size)) + + "vector=" + Arrays.toString(Arrays.copyOf(vector, this.size)) + '}'; } } -- cgit v1.2.3-54-g00ecf From 5154a8055112de064ebf3bcf5be8065ac3dc8b7b Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 8 Aug 2023 19:47:51 +0200 Subject: SuppressWarnings for using deterministic random in hash code generation. --- .../store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java | 1 + 1 file changed, 1 insertion(+) (limited to 'subprojects') 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 8fcf24b1..0a40a19d 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 @@ -24,6 +24,7 @@ public abstract class AbstractNeighbourhoodCalculator { protected AbstractNeighbourhoodCalculator(List> interpretations, IntSet individuals) { this.nullImpactValues = new ArrayList<>(); this.impactValues = new LinkedHashMap<>(); + @SuppressWarnings("squid:S2245") Random random = new Random(1); individualHashValues = new IntLongHashMap(); -- cgit v1.2.3-54-g00ecf From eef4d3b16789e19c35be38e012d45540a226eca1 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 8 Aug 2023 20:18:12 +0200 Subject: Unused condition simplified. --- .../store/statecoding/stateequivalence/CombinationNodePairing.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'subprojects') 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 2877bd0f..decff1d5 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 @@ -57,8 +57,7 @@ public class CombinationNodePairing implements NodePairing { for (var permutation : previousPermutations) { for (int pos = 0; pos <= max; pos++) { int[] newPermutation = new int[max + 1]; - if (pos >= 0) - System.arraycopy(permutation, 0, newPermutation, 0, pos); + System.arraycopy(permutation, 0, newPermutation, 0, pos); newPermutation[pos] = max; if (max - (pos + 1) >= 0) System.arraycopy(permutation, pos + 1, newPermutation, pos + 1 + 1, max - (pos + 1)); -- cgit v1.2.3-54-g00ecf From e2268925741bd4c68dcead708b211bf5fd99440c Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 8 Aug 2023 20:19:03 +0200 Subject: Added test for StateEquivalenceChecker Unknown outcome. --- .../StateEquivalenceCheckerImpl.java | 6 +++- .../store/statecoding/EquivalenceTest.java | 39 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) (limited to 'subprojects') 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 34dba34e..ef0d76a7 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 @@ -58,7 +58,11 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { tried++; } while (hasNext); - return EquivalenceResult.DIFFERENT; + if(permutations == EquivalenceResult.UNKNOWN) { + return EquivalenceResult.UNKNOWN; + } else { + return EquivalenceResult.DIFFERENT; + } } private LongObjectHashMap indexByHash(ObjectCode code, IntSet individuals) { diff --git a/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java b/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java index 8a9c0e9b..3c35849e 100644 --- a/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java @@ -10,6 +10,7 @@ import tools.refinery.store.map.Version; import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.store.representation.Symbol; +import tools.refinery.store.statecoding.neighbourhood.ObjectCodeImpl; import tools.refinery.store.tuple.Tuple; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -175,4 +176,42 @@ class EquivalenceTest { assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v2, v4)); } + + @Test + void largeUnknownTest() { + final int limit = 100; + + StateCodeCalculator calculator = () -> { + var code = new ObjectCodeImpl(); + for (int i = 0; i < limit; i++) { + code.set(i, 1); + } + return new StateCoderResult(1, code); + }; + + ModelStore store = ModelStore.builder() + .symbols(person, age, friend, parents, population) + .with(StateCoderAdapter.builder() + .stateCodeCalculatorFactory((p1, p2) -> calculator)) + .build(); + + var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); + Model model = createStore().createEmptyModel(); + + var personI = model.getInterpretation(person); + var friendI = model.getInterpretation(friend); + + for (int i = 0; i < limit; i++) { + personI.put(Tuple.of(i), true); + } + + friendI.put(Tuple.of(11,12),true); + var v1 = model.commit(); + + friendI.put(Tuple.of(11,12),false); + friendI.put(Tuple.of(21,22),false); + var v2 = model.commit(); + + assertEquals(StateEquivalenceChecker.EquivalenceResult.UNKNOWN, stateCoder.checkEquivalence(v1,v2)); + } } -- cgit v1.2.3-54-g00ecf From 2dae95bdf2ea309f7b8a172698d5b9c26ca5c7e5 Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Tue, 8 Aug 2023 21:49:23 +0200 Subject: Improve BestFirstStrategy performance --- .../main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java | 1 - 1 file changed, 1 deletion(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java index 047b204a..92d878ce 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java @@ -195,7 +195,6 @@ public class BestFirstStrategy implements Strategy { } // State is fully traversed. - trajectoriesToExplore.remove(currentTrajectoryWithFitness); currentTrajectoryWithFitness = null; } -- cgit v1.2.3-54-g00ecf From 3839b0a04b8f7872093cb94122c6b1f88f5168f6 Mon Sep 17 00:00:00 2001 From: Attila Ficsor Date: Tue, 8 Aug 2023 22:20:51 +0200 Subject: Add return value to fireRandomActivation --- .../tools/refinery/store/dse/DesignSpaceExplorationAdapter.java | 2 +- .../store/dse/internal/DesignSpaceExplorationAdapterImpl.java | 9 +++------ .../tools/refinery/store/dse/strategy/DepthFirstStrategy.java | 4 +++- 3 files changed, 7 insertions(+), 8 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java index c45f088a..5aed5298 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java @@ -50,7 +50,7 @@ public interface DesignSpaceExplorationAdapter extends ModelAdapter { public boolean fireActivation(Activation activation); - public void fireRandomActivation(); + public boolean fireRandomActivation(); public List getTrajectory(); diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java index 04bba885..220f0b2d 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java @@ -252,7 +252,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration if (isNewState) { modelVisualizerAdapter.addState(newState, getFitness().values()); } - // TODO: Change to this: modelVisualizerAdapter.addTransition(previousState, newState, activation.transformationRule().getName(), activation.activation()); } @@ -260,11 +259,10 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } @Override - public void fireRandomActivation() { + public boolean fireRandomActivation() { var activations = getUntraversedActivations(); if (activations.isEmpty()) { -// TODO: throw exception - return; + return false; } int index = random.nextInt(activations.size()); var iterator = activations.iterator(); @@ -272,7 +270,7 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration iterator.next(); } var activationId = iterator.next(); - fireActivation(activationId); + return fireActivation(activationId); } public List getAllActivations() { @@ -284,7 +282,6 @@ public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExploration } public boolean isCurrentStateAlreadyTraversed() { -// TODO: check isomorphism? return !isNewState; } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java index 5f7f61b8..0a0caa7e 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java @@ -67,7 +67,9 @@ public class DepthFirstStrategy implements Strategy { return; } - dseAdapter.fireRandomActivation(); + if (!dseAdapter.fireRandomActivation()) { + return; + } } } -- cgit v1.2.3-54-g00ecf From 9de7ead9602c2e51448c3b6710d13eb5dd2f3b1f Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 5 Sep 2023 07:59:17 +0200 Subject: restructured DSE framework, failing build --- .../store/dse/DesignSpaceExplorationAdapter.java | 68 ----- .../store/dse/DesignSpaceExplorationBuilder.java | 48 ---- .../dse/DesignSpaceExplorationStoreAdapter.java | 29 -- .../java/tools/refinery/store/dse/Strategy.java | 13 - .../refinery/store/dse/internal/Activation.java | 14 - .../DesignSpaceExplorationAdapterImpl.java | 294 --------------------- .../DesignSpaceExplorationBuilderImpl.java | 67 ----- .../DesignSpaceExplorationStoreAdapterImpl.java | 65 ----- .../store/dse/internal/TransformationRule.java | 97 ------- .../dse/modification/ModificationAdapter.java | 24 ++ .../dse/modification/ModificationBuilder.java | 11 + .../dse/modification/ModificationStoreAdapter.java | 11 + .../internal/ModificationAdapterImpl.java | 62 +++++ .../internal/ModificationBuilderImpl.java | 29 ++ .../internal/ModificationStoreAdapterImpl.java | 29 ++ .../AlwaysSatisfiedDummyHardObjective.java | 52 ---- .../AlwaysSatisfiedRandomHardObjective.java | 56 ---- .../store/dse/objectives/BaseObjective.java | 132 --------- .../refinery/store/dse/objectives/Comparators.java | 26 -- .../refinery/store/dse/objectives/Fitness.java | 46 ---- .../refinery/store/dse/objectives/Objective.java | 101 ------- .../dse/objectives/ObjectiveComparatorHelper.java | 60 ----- .../store/dse/strategy/BestFirstExplorer.java | 164 ++++++++++++ .../store/dse/strategy/BestFirstStoreManager.java | 71 +++++ .../store/dse/strategy/BestFirstStrategy.java | 203 -------------- .../store/dse/strategy/BestFirstWorker.java | 113 ++++++++ .../store/dse/strategy/DepthFirstStrategy.java | 92 ------- .../refinery/store/dse/strategy/SubmitResult.java | 14 + .../transition/DesignSpaceExplorationAdapter.java | 31 +++ .../transition/DesignSpaceExplorationBuilder.java | 59 +++++ .../DesignSpaceExplorationStoreAdapter.java | 27 ++ .../store/dse/transition/ObjectiveValue.java | 24 ++ .../store/dse/transition/ObjectiveValues.java | 69 +++++ .../store/dse/transition/Transformation.java | 43 +++ .../store/dse/transition/TransformationRule.java | 63 +++++ .../dse/transition/VersionWithObjectiveValue.java | 11 + .../DesignSpaceExplorationAdapterImpl.java | 90 +++++++ .../DesignSpaceExplorationBuilderImpl.java | 75 ++++++ .../DesignSpaceExplorationStoreAdapterImpl.java | 70 +++++ .../store/dse/transition/objectives/Criterion.java | 15 ++ .../transition/objectives/CriterionCalculator.java | 10 + .../store/dse/transition/objectives/Objective.java | 15 ++ .../transition/objectives/ObjectiveCalculator.java | 10 + .../dse/transition/objectives/QueryCriteria.java | 44 +++ .../dse/transition/objectives/QueryObjective.java | 44 +++ .../dse/transition/statespace/ActivationStore.java | 18 ++ .../statespace/EquivalenceClassStore.java | 16 ++ .../statespace/ObjectivePriorityQueue.java | 21 ++ .../dse/transition/statespace/SolutionStore.java | 17 ++ .../internal/AbstractEquivalenceClassStore.java | 47 ++++ .../internal/ActivationStoreBitVectorEntry.java | 46 ++++ .../statespace/internal/ActivationStoreEntry.java | 32 +++ .../statespace/internal/ActivationStoreImpl.java | 131 +++++++++ .../internal/ActivationStoreListEntry.java | 95 +++++++ .../statespace/internal/ActivationStoreWorker.java | 56 ++++ .../internal/CompleteEquivalenceClassStore.java | 107 ++++++++ .../internal/FastEquivalenceClassStore.java | 32 +++ .../internal/ObjectivePriorityQueueImpl.java | 74 ++++++ .../statespace/internal/SolutionStoreImpl.java | 53 ++++ .../tools/refinery/store/dse/CRAExamplesTest.java | 6 +- .../java/tools/refinery/store/dse/DebugTest.java | 4 +- .../store/dse/DesignSpaceExplorationTest.java | 3 +- .../refinery/store/dse/TransformationRuleTest.java | 3 +- .../statespace/internal/ActivationUnitTest.java | 72 +++++ 64 files changed, 2054 insertions(+), 1470 deletions(-) delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationBuilder.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationStoreAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationBuilderImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java delete mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/SubmitResult.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValue.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValues.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/VersionWithObjectiveValue.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CriterionCalculator.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/ObjectiveCalculator.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ActivationStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ObjectivePriorityQueue.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/SolutionStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java create mode 100644 subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java create mode 100644 subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java deleted file mode 100644 index 5aed5298..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationAdapter.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse; - -import tools.refinery.store.adapter.ModelAdapter; -import tools.refinery.store.map.Version; -import tools.refinery.store.dse.internal.Activation; -import tools.refinery.store.dse.internal.DesignSpaceExplorationBuilderImpl; -import tools.refinery.store.dse.objectives.Fitness; -import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.Tuple1; - -import java.util.Collection; -import java.util.List; -import java.util.Random; - -public interface DesignSpaceExplorationAdapter extends ModelAdapter { - @Override - DesignSpaceExplorationStoreAdapter getStoreAdapter(); - - static DesignSpaceExplorationBuilder builder() { - return new DesignSpaceExplorationBuilderImpl(); - } - - Collection explore(); - - public int getModelSize(); - - public Tuple1 createObject(); - - public Tuple deleteObject(Tuple tuple); - - public boolean checkGlobalConstraints(); - - public boolean backtrack(); - - public boolean backtrack(String reason); - - public Fitness getFitness(); - - public void newSolution(); - - public int getDepth(); - - public Collection getUntraversedActivations(); - - public boolean fireActivation(Activation activation); - - public boolean fireRandomActivation(); - - public List getTrajectory(); - - public boolean isCurrentStateAlreadyTraversed(); - - public ObjectiveComparatorHelper getObjectiveComparatorHelper(); - - public void restoreTrajectory(List trajectory); - - public void setRandom(Random random); - - public void setRandom(long seed); - - public List getSolutions(); -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java deleted file mode 100644 index 4def5cb2..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationBuilder.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse; - -import tools.refinery.store.adapter.ModelAdapterBuilder; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.internal.TransformationRule; -import tools.refinery.store.dse.objectives.Objective; - -import java.util.Collection; -import java.util.List; - -public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { - default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRules) { - return transformations(List.of(transformationRules)); - } - - default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { - transformationRules.forEach(this::transformation); - return this; - } - - default DesignSpaceExplorationBuilder globalConstraints(RelationalQuery... globalConstraints) { - return globalConstraints(List.of(globalConstraints)); - } - - default DesignSpaceExplorationBuilder globalConstraints(Collection globalConstraints) { - globalConstraints.forEach(this::globalConstraint); - return this; - } - - default DesignSpaceExplorationBuilder objectives(Objective... objectives) { - return objectives(List.of(objectives)); - } - - default DesignSpaceExplorationBuilder objectives(Collection objectives) { - objectives.forEach(this::objective); - return this; - } - - DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule); - DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint); - DesignSpaceExplorationBuilder objective(Objective objective); - DesignSpaceExplorationBuilder strategy(Strategy strategy); -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java deleted file mode 100644 index 0252748d..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/DesignSpaceExplorationStoreAdapter.java +++ /dev/null @@ -1,29 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse; - -import tools.refinery.store.adapter.ModelStoreAdapter; -import tools.refinery.store.dse.internal.TransformationRule; -import tools.refinery.store.dse.objectives.Objective; -import tools.refinery.store.model.Model; -import tools.refinery.store.query.dnf.RelationalQuery; - -import java.util.List; -import java.util.Set; - -public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter { - - @Override - DesignSpaceExplorationAdapter createModelAdapter(Model model); - - Set getTransformationSpecifications(); - - Set getGlobalConstraints(); - - List getObjectives(); - - Strategy getStrategy(); -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java deleted file mode 100644 index c60a4410..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/Strategy.java +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse; - -public interface Strategy { - - void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter); - - void explore(); -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java deleted file mode 100644 index 1893ce2e..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/Activation.java +++ /dev/null @@ -1,14 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.internal; - -import tools.refinery.store.tuple.Tuple; - -public record Activation(TransformationRule transformationRule, Tuple activation) { - public boolean fire() { - return transformationRule.fireActivation(activation); - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java deleted file mode 100644 index 220f0b2d..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationAdapterImpl.java +++ /dev/null @@ -1,294 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.internal; - -import tools.refinery.store.map.Version; -import tools.refinery.store.model.Interpretation; -import tools.refinery.store.model.Model; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; -import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter; -import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.objectives.Fitness; -import tools.refinery.store.dse.objectives.Objective; -import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper; -import tools.refinery.store.query.resultset.ResultSet; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.store.tuple.Tuple1; -import tools.refinery.visualization.ModelVisualizerAdapter; - -import java.util.*; - -public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExplorationAdapter { - static final Symbol NODE_COUNT_SYMBOL = Symbol.of("MODEL_SIZE", 0, Integer.class, 0); - private final Model model; - private final ModelQueryAdapter queryEngine; - private final DesignSpaceExplorationStoreAdapterImpl storeAdapter; - private final Set transformationRules; - private final Set globalConstraints; - private final List objectives; - private final LinkedHashSet> globalConstraintResultSets = new LinkedHashSet<>(); - private final Interpretation sizeInterpretation; - private final Strategy strategy; - - private ObjectiveComparatorHelper objectiveComparatorHelper; - private List trajectory = new ArrayList<>(); - private Map parents = new HashMap<>(); - private final List solutions = new ArrayList<>(); - private Map> statesAndTraversedActivations; - private Random random = new Random(); - private boolean isNewState = false; - private final boolean isVisualizationEnabled; - private final ModelVisualizerAdapter modelVisualizerAdapter; - - private final Map fitnessCache = new HashMap<>(); - - public DesignSpaceExplorationAdapterImpl(Model model, DesignSpaceExplorationStoreAdapterImpl storeAdapter) { - this.model = model; - this.storeAdapter = storeAdapter; - this.sizeInterpretation = model.getInterpretation(NODE_COUNT_SYMBOL); - queryEngine = model.getAdapter(ModelQueryAdapter.class); - - globalConstraints = storeAdapter.getGlobalConstraints(); - for (var constraint : globalConstraints) { - globalConstraintResultSets.add(queryEngine.getResultSet(constraint)); - } - - transformationRules = storeAdapter.getTransformationSpecifications(); - for (var rule : transformationRules) { - rule.prepare(model, queryEngine); - } - - objectives = storeAdapter.getObjectives(); - statesAndTraversedActivations = new HashMap<>(); - strategy = storeAdapter.getStrategy(); - strategy.initialize(this); - modelVisualizerAdapter = model.tryGetAdapter(ModelVisualizerAdapter.class).orElse(null); - isVisualizationEnabled = modelVisualizerAdapter != null; - - } - - public List getTrajectory() { - return new ArrayList<>(trajectory); - } - - @Override - public Model getModel() { - return model; - } - - @Override - public DesignSpaceExplorationStoreAdapter getStoreAdapter() { - return storeAdapter; - } - - @Override - public List explore() { - var state = model.commit(); - trajectory.add(state); - strategy.explore(); - if (isVisualizationEnabled) { - modelVisualizerAdapter.visualize(); - } - return solutions; - } - - @Override - public int getModelSize() { - return sizeInterpretation.get(Tuple.of()); - } - - @Override - public Tuple1 createObject() { - var newNodeId = getModelSize(); - sizeInterpretation.put(Tuple.of(), newNodeId + 1); - return Tuple.of(newNodeId); - } - - @Override - public Tuple deleteObject(Tuple tuple) { - if (tuple.getSize() != 1) { - throw new IllegalArgumentException("Tuple size must be 1"); - } -// TODO: implement more efficient deletion -// if (tuple.get(0) == getModelSize() - 1) { -// sizeInterpretation.put(Tuple.of(), getModelSize() - 1); -// } - return tuple; - } - - @Override - public boolean checkGlobalConstraints() { - for (var resultSet : globalConstraintResultSets) { - if (resultSet.size() > 0) { - return false; - } - } - return true; - } - - @Override - public boolean backtrack() { - return backtrack(""); - } - @Override - public boolean backtrack(String reason) { - if (trajectory.size() < 2) { - return false; - } - var currentState = model.getState(); - if (!parents.containsKey(currentState)) { - return false; - } - if (isVisualizationEnabled) { - modelVisualizerAdapter.addTransition(trajectory.get(trajectory.size() - 1), - trajectory.get(trajectory.size() - 2), "backtrack(" + reason + ")"); - } - model.restore(parents.get(model.getState())); - trajectory.remove(trajectory.size() - 1); - return true; - } - - @Override - public void restoreTrajectory(List trajectory) { - model.restore(trajectory.get(trajectory.size() - 1)); -// if (isVisualizationEnabled) { -// modelVisualizerAdapter.addTransition(this.trajectory.get(trajectory.size() - 1), -// trajectory.get(trajectory.size() - 1), "restore"); -// } - this.trajectory = new ArrayList<>(trajectory); - - } - - @Override - public void setRandom(Random random) { - this.random = random; - } - - @Override - public void setRandom(long seed) { - this.random = new Random(seed); - } - - @Override - public List getSolutions() { - return solutions; - } - - @Override - public Fitness getFitness() { - return fitnessCache.computeIfAbsent(model.getState(), s -> calculateFitness()); - } - - private Fitness calculateFitness() { - Fitness result = new Fitness(); - boolean satisfiesHardObjectives = true; - for (Objective objective : objectives) { - var fitness = objective.getFitness(this); - result.put(objective.getName(), fitness); - if (objective.isHardObjective() && !objective.satisfiesHardObjective(fitness)) { - satisfiesHardObjectives = false; - } - } - result.setSatisfiesHardObjectives(satisfiesHardObjectives); - - return result; - } - - @Override - public void newSolution() { - var state = model.getState(); - solutions.add(state); - if (isVisualizationEnabled) { - modelVisualizerAdapter.addSolution(state); - } - } - - @Override - public int getDepth() { - return trajectory.size() - 1; - } - - public LinkedHashSet getUntraversedActivations() { - var traversedActivations = statesAndTraversedActivations.get(model.getState()); - if (traversedActivations == null) { - return new LinkedHashSet<>(getAllActivations()); - } - else { - LinkedHashSet untraversedActivations = new LinkedHashSet<>(); - for (Activation activation : getAllActivations()) { - if (!traversedActivations.contains(activation)) { - untraversedActivations.add(activation); - } - } - return untraversedActivations; - } - } - - @Override - public boolean fireActivation(Activation activation) { - if (activation == null) { - return false; - } - var previousState = model.getState(); - if (!activation.fire()) { - return false; - } - statesAndTraversedActivations.computeIfAbsent(previousState, s -> new ArrayList<>()).add(activation); - var newState = model.commit(); - trajectory.add(newState); - parents.put(newState, previousState); - isNewState = !statesAndTraversedActivations.containsKey(newState); - if (isVisualizationEnabled) { - if (isNewState) { - modelVisualizerAdapter.addState(newState, getFitness().values()); - } - modelVisualizerAdapter.addTransition(previousState, newState, activation.transformationRule().getName(), - activation.activation()); - } - return true; - } - - @Override - public boolean fireRandomActivation() { - var activations = getUntraversedActivations(); - if (activations.isEmpty()) { - return false; - } - int index = random.nextInt(activations.size()); - var iterator = activations.iterator(); - while (index-- > 0) { - iterator.next(); - } - var activationId = iterator.next(); - return fireActivation(activationId); - } - - public List getAllActivations() { - List result = new LinkedList<>(); - for (var rule : transformationRules) { - result.addAll(rule.getAllActivationsAsList()); - } - return result; - } - - public boolean isCurrentStateAlreadyTraversed() { - return !isNewState; - } - - public ObjectiveComparatorHelper getObjectiveComparatorHelper() { - if (objectiveComparatorHelper == null) { - objectiveComparatorHelper = new ObjectiveComparatorHelper(objectives); - } - return objectiveComparatorHelper; - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java deleted file mode 100644 index 8f7056f2..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationBuilderImpl.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.internal; - -import tools.refinery.store.adapter.AbstractModelAdapterBuilder; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.model.ModelStoreBuilder; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.DesignSpaceExplorationBuilder; -import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.objectives.Objective; - -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; - -public class DesignSpaceExplorationBuilderImpl - extends AbstractModelAdapterBuilder - implements DesignSpaceExplorationBuilder { - private final LinkedHashSet transformationSpecifications = new LinkedHashSet<>(); - private final LinkedHashSet globalConstraints = new LinkedHashSet<>(); - private final List objectives = new LinkedList<>(); - private Strategy strategy; - - @Override - protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) { - return new DesignSpaceExplorationStoreAdapterImpl(store, transformationSpecifications, globalConstraints, - objectives, strategy); - } - - @Override - public DesignSpaceExplorationBuilder transformation(TransformationRule transformationRule) { - checkNotConfigured(); - transformationSpecifications.add(transformationRule); - return this; - } - - @Override - public DesignSpaceExplorationBuilder globalConstraint(RelationalQuery globalConstraint) { - checkNotConfigured(); - globalConstraints.add(globalConstraint); - return this; - } - - @Override - public DesignSpaceExplorationBuilder objective(Objective objective) { - checkNotConfigured(); - objectives.add(objective); - return this; - } - - @Override - public DesignSpaceExplorationBuilder strategy(Strategy strategy) { - checkNotConfigured(); - this.strategy = strategy; - return this; - } - - @Override - protected void doConfigure(ModelStoreBuilder storeBuilder) { - storeBuilder.symbols(DesignSpaceExplorationAdapterImpl.NODE_COUNT_SYMBOL); - super.doConfigure(storeBuilder); - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java deleted file mode 100644 index fea39886..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/DesignSpaceExplorationStoreAdapterImpl.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.internal; - -import tools.refinery.store.dse.DesignSpaceExplorationStoreAdapter; -import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.objectives.Objective; -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.dnf.RelationalQuery; - -import java.util.List; -import java.util.Set; - -public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { - private final ModelStore store; - private final Set transformationSpecifications; - private final Set globalConstraints; - private final List objectives; - private final Strategy strategy; - - public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, - Set transformationSpecifications, - Set globalConstraints, - List objectives, Strategy strategy) { - this.store = store; - this.transformationSpecifications = transformationSpecifications; - this.globalConstraints = globalConstraints; - this.objectives = objectives; - this.strategy = strategy; - } - - @Override - public ModelStore getStore() { - return store; - } - - @Override - public DesignSpaceExplorationAdapterImpl createModelAdapter(Model model) { - return new DesignSpaceExplorationAdapterImpl(model, this); - } - - @Override - public Set getTransformationSpecifications() { - return transformationSpecifications; - } - - @Override - public Set getGlobalConstraints() { - return globalConstraints; - } - - @Override - public List getObjectives() { - return objectives; - } - - @Override - public Strategy getStrategy() { - return strategy; - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java deleted file mode 100644 index 8123c0d6..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/internal/TransformationRule.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.internal; - -import org.eclipse.collections.api.block.procedure.Procedure; -import tools.refinery.store.model.Model; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.ActionFactory; -import tools.refinery.store.query.resultset.OrderedResultSet; -import tools.refinery.store.query.resultset.ResultSet; -import tools.refinery.store.tuple.Tuple; - -import java.util.*; - -public class TransformationRule { - - private final String name; - private final RelationalQuery precondition; - private final ActionFactory actionFactory; - private Procedure action; - private OrderedResultSet activations; - private Random random; - private ModelQueryAdapter queryEngine; - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) { - this(name, precondition, actionFactory, new Random()); - } - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) { - this(name, precondition, actionFactory, new Random(seed)); - } - - public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) { - this.name = name; - this.precondition = precondition; - this.actionFactory = actionFactory; - this.random = random; - } - public boolean prepare(Model model, ModelQueryAdapter queryEngine) { - action = actionFactory.prepare(model); - this.queryEngine = queryEngine; - activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); - return true; - } - - public boolean fireActivation(Tuple activation) { - action.accept(activation); - queryEngine.flushChanges(); - return true; - } - - public boolean fireRandomActivation() { - return getRandomActivation().fire(); - } - - public String getName() { - return name; - } - - public RelationalQuery getPrecondition() { - return precondition; - } - - public ResultSet getAllActivationsAsResultSet() { - return activations; - } - - public Set getAllActivations() { - var result = new LinkedHashSet(); - var cursor = activations.getAll(); - while (cursor.move()) { - result.add(new Activation(this, cursor.getKey())); - } - return result; - } - - public List getAllActivationsAsList() { - var result = new ArrayList(); - var cursor = activations.getAll(); - while (cursor.move()) { - result.add(new Activation(this, cursor.getKey())); - } - return result; - } - - public Activation getRandomActivation() { - return new Activation(this, activations.getKey(random.nextInt(activations.size()))); - } - - public Activation getActivation(int index) { - return new Activation(this, activations.getKey(index)); - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java new file mode 100644 index 00000000..f15c16e0 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationAdapter.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.dse.modification.internal.ModificationBuilderImpl; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; + +public interface ModificationAdapter extends ModelAdapter { + + int getModelSize(); + + Tuple1 createObject(); + + Tuple deleteObject(Tuple tuple); + + static ModificationBuilder builder() { + return new ModificationBuilderImpl(); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationBuilder.java new file mode 100644 index 00000000..48c22bdf --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationBuilder.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification; + +import tools.refinery.store.adapter.ModelAdapterBuilder; + +public interface ModificationBuilder extends ModelAdapterBuilder { +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationStoreAdapter.java new file mode 100644 index 00000000..144c4d05 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/ModificationStoreAdapter.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification; + +import tools.refinery.store.adapter.ModelStoreAdapter; + +public interface ModificationStoreAdapter extends ModelStoreAdapter { +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java new file mode 100644 index 00000000..b2a80d71 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationAdapterImpl.java @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.internal; + +import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.dse.modification.ModificationAdapter; +import tools.refinery.store.model.Interpretation; +import tools.refinery.store.model.Model; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; + +public class ModificationAdapterImpl implements ModificationAdapter { + static final Symbol NEXT_ID = Symbol.of("NEXT_ID", 0, Integer.class, 0); + + final ModelStoreAdapter storeAdapter; + final Model model; + Interpretation nodeCountInterpretation; + + ModificationAdapterImpl(ModelStoreAdapter storeAdapter, Model model) { + this.storeAdapter = storeAdapter; + this.model = model; + this.nodeCountInterpretation = model.getInterpretation(NEXT_ID); + } + + @Override + public Model getModel() { + return model; + } + + @Override + public ModelStoreAdapter getStoreAdapter() { + return storeAdapter; + } + + @Override + public int getModelSize() { + return nodeCountInterpretation.get(Tuple.of()); + } + + @Override + public Tuple1 createObject() { + var newNodeId = getModelSize(); + nodeCountInterpretation.put(Tuple.of(), newNodeId + 1); + return Tuple.of(newNodeId); + } + + @Override + public Tuple deleteObject(Tuple tuple) { + if (tuple.getSize() != 1) { + throw new IllegalArgumentException("Tuple size must be 1"); + } +// TODO: implement more efficient deletion + if (tuple.get(0) == getModelSize() - 1) { + nodeCountInterpretation.put(Tuple.of(), getModelSize() - 1); + } + return tuple; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationBuilderImpl.java new file mode 100644 index 00000000..c4d38d22 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationBuilderImpl.java @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.internal; + +import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.dse.modification.ModificationBuilder; +import tools.refinery.store.dse.modification.ModificationStoreAdapter; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.statecoding.StateCoderBuilder; + +public class ModificationBuilderImpl extends AbstractModelAdapterBuilder implements ModificationBuilder { + + @Override + protected void doConfigure(ModelStoreBuilder storeBuilder) { + storeBuilder.symbols(ModificationAdapterImpl.NEXT_ID); + storeBuilder.tryGetAdapter(StateCoderBuilder.class).ifPresent( + coderBuilder -> coderBuilder.exclude(ModificationAdapterImpl.NEXT_ID)); + super.doConfigure(storeBuilder); + } + + @Override + protected ModificationStoreAdapter doBuild(ModelStore store) { + return new ModificationStoreAdapterImpl(store); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java new file mode 100644 index 00000000..913cb33f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/modification/internal/ModificationStoreAdapterImpl.java @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.modification.internal; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.dse.modification.ModificationStoreAdapter; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; + +public class ModificationStoreAdapterImpl implements ModificationStoreAdapter { + ModelStore store; + + ModificationStoreAdapterImpl(ModelStore store) { + this.store = store; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public ModelAdapter createModelAdapter(Model model) { + return null; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java deleted file mode 100644 index afed75fd..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedDummyHardObjective.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; - -/** - * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution. - * - * @author Andras Szabolcs Nagy - * - */ -public class AlwaysSatisfiedDummyHardObjective extends BaseObjective { - - private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective"; - - public AlwaysSatisfiedDummyHardObjective() { - super(DEFAULT_NAME); - } - - public AlwaysSatisfiedDummyHardObjective(String name) { - super(name); - } - - @Override - public Double getFitness(DesignSpaceExplorationAdapter context) { - return 0d; - } - - @Override - public boolean isHardObjective() { - return true; - } - - @Override - public boolean satisfiesHardObjective(Double fitness) { - return true; - } - - @Override - public Objective createNew() { - return this; - } - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java deleted file mode 100644 index 327d5e2f..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/AlwaysSatisfiedRandomHardObjective.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; - -import java.util.Random; - -/** - * This hard objective is fulfilled in any circumstances. Use it if all states should be regarded as a valid solution. - * - * @author Andras Szabolcs Nagy - * - */ -public class AlwaysSatisfiedRandomHardObjective extends BaseObjective { - - private static final String DEFAULT_NAME = "AlwaysSatisfiedDummyHardObjective"; - private static final Random random = new Random(0); - - public AlwaysSatisfiedRandomHardObjective() { - super(DEFAULT_NAME); - } - - public AlwaysSatisfiedRandomHardObjective(String name) { - super(name); - } - - @Override - public Double getFitness(DesignSpaceExplorationAdapter context) { -// return 0d; - return random.nextDouble(); - } - - @Override - public boolean isHardObjective() { - return true; - } - - @Override - public boolean satisfiesHardObjective(Double fitness) { - return true; - } - - @Override - public Objective createNew() { - return this; - } - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java deleted file mode 100644 index b76598fb..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/BaseObjective.java +++ /dev/null @@ -1,132 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; - -import java.util.Comparator; -import java.util.Objects; - -/** - * This abstract class implements the basic functionality of an objective ({@link Objective} namely its name, - * comparator, level and fitness hard constraint. - * - * @author Andras Szabolcs Nagy - * - */ -public abstract class BaseObjective implements Objective { - - protected final String name; - protected Comparator comparator = Comparators.HIGHER_IS_BETTER; - - protected double fitnessConstraint; - protected boolean isThereFitnessConstraint = false; - protected Comparator fitnessConstraintComparator; - - protected BaseObjective(String name) { - Objects.requireNonNull(name, "Name of the objective cannot be null."); - this.name = name; - } - - @Override - public String getName() { - return name; - } - - @Override - public void setComparator(Comparator comparator) { - this.comparator = comparator; - } - - @Override - public Comparator getComparator() { - return comparator; - } - - public BaseObjective withComparator(Comparator comparator) { - setComparator(comparator); - return this; - } - - /** - * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the - * current state as a solution. - * - * @param fitnessConstraint - * Solutions should be better than this value. - * @param fitnessConstraintComparator - * {@link Comparator} to determine if the current state is better than the given value. - * @return The actual instance to enable builder pattern like usage. - */ - public BaseObjective withHardConstraintOnFitness(double fitnessConstraint, - Comparator fitnessConstraintComparator) { - this.fitnessConstraint = fitnessConstraint; - this.fitnessConstraintComparator = fitnessConstraintComparator; - this.isThereFitnessConstraint = true; - return this; - } - - /** - * Adds a hard constraint on the fitness value. For example, the fitness value must be better than 10 to accept the - * current state as a solution. The provided comparator will be used. - * - * @param fitnessConstraint - * Solutions should be better than this value. - * @return The actual instance to enable builder pattern like usage. - */ - public BaseObjective withHardConstraintOnFitness(double fitnessConstraint) { - return withHardConstraintOnFitness(fitnessConstraint, null); - } - - @Override - public void init(DesignSpaceExplorationAdapter context) { - if (fitnessConstraintComparator == null) { - fitnessConstraintComparator = comparator; - } - } - - @Override - public boolean isHardObjective() { - return isThereFitnessConstraint; - } - - @Override - public boolean satisfiesHardObjective(Double fitness) { - if (isThereFitnessConstraint) { - int compare = fitnessConstraintComparator.compare(fitness, fitnessConstraint); - if (compare < 0) { - return false; - } - } - return true; - } - - @Override - public int hashCode() { - return name.hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if (obj instanceof BaseObjective baseObjective) { - return name.equals(baseObjective.getName()); - } - return false; - } - - @Override - public String toString() { - return name; - } - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java deleted file mode 100644 index 181397b3..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Comparators.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import java.util.Comparator; - -public class Comparators { - - private Comparators() { /*Utility class constructor*/ } - - public static final Comparator HIGHER_IS_BETTER = Double::compareTo; - - public static final Comparator LOWER_IS_BETTER = (o1, o2) -> o2.compareTo(o1); - - private static final Double ZERO = (double) 0; - - public static final Comparator DIFFERENCE_TO_ZERO_IS_BETTER = (o1, o2) -> ZERO.compareTo(Math.abs(o1)-Math.abs(o2)); - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java deleted file mode 100644 index b1dc4442..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Fitness.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import java.util.HashMap; - -public class Fitness extends HashMap { - - private boolean satisfiesHardObjectives; - - public boolean isSatisfiesHardObjectives() { - return satisfiesHardObjectives; - } - - public void setSatisfiesHardObjectives(boolean satisfiesHardObjectives) { - this.satisfiesHardObjectives = satisfiesHardObjectives; - } - - @Override - public String toString() { - return super.toString() + " hardObjectives=" + satisfiesHardObjectives; - } - - @Override - public boolean equals(Object other) { - if (other == null) return false; - if (getClass() != other.getClass()) return false; - if (!super.equals(other)) return false; - return satisfiesHardObjectives == ((Fitness) other).satisfiesHardObjectives; - } - - @Override - public int hashCode() { - int h = super.hashCode(); - h = h * 31 + (satisfiesHardObjectives ? 1 : 0); - return h; - } - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java deleted file mode 100644 index c7313622..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/Objective.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; - -import java.util.Comparator; - -/** - * - * Implementation of this interface represents a single objective of the DSE problem, which can assess a solution - * (trajectory) in a single number. It has a name and a comparator which orders two solution based on the calculated - * value. - *

- * Objectives can be either hard or soft objectives. Hard objectives can be satisfied or unsatisfied. If all of the hard - * objectives are satisfied on a single solution, then it is considered to be a valid (or goal) solution. - *

- * Certain objectives can have inner state for calculating the fitness value. In this case a new instance is necessary - * for every new thread, and the {@code createNew} method should not return the same instance more than once. - * - * @author Andras Szabolcs Nagy - * - */ -public interface Objective { - - /** - * Returns the name of the objective. - * - * @return The name of the objective. - */ - String getName(); - - /** - * Sets the {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is to - * minimize or maximize (or minimize or maximize a delta from a given number). - * - * @param comparator The comparator. - */ - void setComparator(Comparator comparator); - - /** - * Returns a {@link Comparator} which is used to compare fitness (doubles). It determines whether the objective is - * to minimize or maximize (or minimize or maximize a delta from a given number). - * - * @return The comparator. - */ - Comparator getComparator(); - - /** - * Calculates the value of the objective on a given solution (trajectory). - * - * @param context - * The {@link DesignSpaceExplorationAdapter} - * @return The objective value in double. - */ - Double getFitness(DesignSpaceExplorationAdapter context); - - /** - * Initializes the objective. It is called exactly once for every thread starts. - * - * @param context - * The {@link DesignSpaceExplorationAdapter}. - */ - void init(DesignSpaceExplorationAdapter context); - - /** - * Returns an instance of the {@link Objective}. If it returns the same instance, all the methods has to be thread - * save as they are called concurrently. - * - * @return An instance of the objective. - */ - Objective createNew(); - - /** - * Returns true if the objective is a hard objective. In such a case the method - * {@link Objective#satisfiesHardObjective(Double)} is called. - * - * @return True if the objective is a hard objective. - * @see Objective#satisfiesHardObjective(Double) - * @see Objective - */ - boolean isHardObjective(); - - /** - * Determines if the given fitness value satisfies the hard objective. - * - * @param fitness - * The fitness value of a solution. - * @return True if it satisfies the hard objective or it is a soft constraint. - * @see Objective - */ - boolean satisfiesHardObjective(Double fitness); - -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java deleted file mode 100644 index eb03eeaf..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/objectives/ObjectiveComparatorHelper.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.objectives; - -import java.util.List; - -/** - * This class is responsible to compare and sort fitness values. - * - * @author András Szabolcs Nagy - */ -public class ObjectiveComparatorHelper { - - private final List objectives; - - public ObjectiveComparatorHelper(List objectives) { - this.objectives = objectives; - } - - /** - * Compares two fitnesses based on dominance. Returns -1 if the second parameter {@code o2} is a better - * solution ({@code o2} dominates {@code o1}), 1 if the first parameter {@code o1} is better ({@code o1} dominates - * {@code o2}) and returns 0 if they are non-dominating each other. - */ - public int compare(Fitness o1, Fitness o2) { - - boolean o1HasBetterFitness = false; - boolean o2HasBetterFitness = false; - - for (Objective objective : objectives) { - String objectiveName = objective.getName(); - int sgn = objective.getComparator().compare(o1.get(objectiveName), o2.get(objectiveName)); - - if (sgn < 0) { - o2HasBetterFitness = true; - } - if (sgn > 0) { - o1HasBetterFitness = true; - } - if (o1HasBetterFitness && o2HasBetterFitness) { - break; - } - } - if (o2HasBetterFitness) { - return -1; - } else if (o1HasBetterFitness) { - return 1; - } - - return 0; - - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java new file mode 100644 index 00000000..72bbbc55 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstExplorer.java @@ -0,0 +1,164 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.strategy; + +import tools.refinery.store.dse.transition.ObjectiveValue; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.model.Model; + +import java.util.Random; + +public class BestFirstExplorer extends BestFirstWorker { + final int id; + Random random; + public BestFirstExplorer(BestFirstStoreManager storeManager, Model model, int id) { + super(storeManager, model); + this.id = id; + this.random = new Random(id); + } + + private boolean interrupted = false; + public void interrupt() { + this.interrupted = true; + } + + private boolean shouldRun() { + return !interrupted && !hasEnoughSolution(); + } + + public void explore() { + VersionWithObjectiveValue lastVisited = submit().newVersion(); + + mainLoop: while (shouldRun()) { + + if (lastVisited == null) { + var restored = this.restoreToBest(); + if(restored != null) { + lastVisited = restored; + } else { + return; + } + } + + boolean tryActivation = true; + while(tryActivation && shouldRun()) { + RandomVisitResult randomVisitResult = this.visitRandomUnvisited(random); + + tryActivation &= randomVisitResult.shouldRetry(); + var newSubmit = randomVisitResult.submitResult(); + if(newSubmit != null) { + if(!newSubmit.include()) { + restoreToLast(); + } else { + var newVisit = newSubmit.newVersion(); + int compareResult = compare(lastVisited,newVisit); + if(compareResult >= 0) { + lastVisited = newVisit; + continue mainLoop; + } + } + } + } + + //final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); + + /*boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); + if (!globalConstraintsAreSatisfied) { + // Global constraint is not satisfied in the first state. Terminate. + return; + } + + final Fitness firstFitness = dseAdapter.getFitness(); + if (firstFitness.isSatisfiesHardObjectives()) { + dseAdapter.newSolution(); + // First state is a solution. Terminate. + if (backTrackIfSolution) { + return; + } + } + + if (maxDepth == 0) { + return; + }*/ + + /* + var firstTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), firstFitness); + trajectoriesToExplore.add(firstTrajectoryWithFitness); + TrajectoryWithFitness currentTrajectoryWithFitness = null; + */ +/* + Collection activations = dseAdapter.getUntraversedActivations(); + Iterator iterator = activations.iterator(); + + while (iterator.hasNext()) { + final Activation nextActivation = iterator.next(); + if (!iterator.hasNext()) { + // Last untraversed activation of the state. + trajectoriesToExplore.remove(currentTrajectoryWithFitness); + } + + // Executing new activation + dseAdapter.fireActivation(nextActivation); + if (dseAdapter.isCurrentStateAlreadyTraversed()) { + // The new state is already visited. + dseAdapter.backtrack(); + } else if (!dseAdapter.checkGlobalConstraints()) { + // Global constraint is not satisfied. + dseAdapter.backtrack(); + } else { + final Fitness nextFitness = dseAdapter.getFitness(); + if (nextFitness.isSatisfiesHardObjectives()) { + dseAdapter.newSolution(); + var solutions = dseAdapter.getSolutions().size(); + if (solutions >= maxSolutions) { + return; + } + // Found a solution. + if (backTrackIfSolution) { + dseAdapter.backtrack(); + continue; + } + } + if (dseAdapter.getDepth() >= maxDepth) { + // Reached max depth. + dseAdapter.backtrack(); + continue; + } + + TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness( + dseAdapter.getTrajectory(), nextFitness); + trajectoriesToExplore.add(nextTrajectoryWithFitness); + + int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness, + nextTrajectoryWithFitness.fitness); + if (compare < 0) { + // Better fitness, moving on + currentTrajectoryWithFitness = nextTrajectoryWithFitness; + continue mainLoop; + } else if (compare == 0) { + if (onlyBetterFirst) { + // Equally good fitness, backtrack + dseAdapter.backtrack(); + } else { + // Equally good fitness, moving on + currentTrajectoryWithFitness = nextTrajectoryWithFitness; + continue mainLoop; + } + } else { + //"Worse fitness + currentTrajectoryWithFitness = null; + continue mainLoop; + } + } + } + + // State is fully traversed. + currentTrajectoryWithFitness = null; +*/ + } + // Interrupted. + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java new file mode 100644 index 00000000..d1fd7884 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStoreManager.java @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.strategy; + +import org.eclipse.collections.api.block.procedure.Procedure; +import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.ActivationStore; +import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore; +import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue; +import tools.refinery.store.dse.transition.statespace.SolutionStore; +import tools.refinery.store.dse.transition.statespace.internal.ActivationStoreImpl; +import tools.refinery.store.dse.transition.statespace.internal.FastEquivalenceClassStore; +import tools.refinery.store.dse.transition.statespace.internal.ObjectivePriorityQueueImpl; +import tools.refinery.store.dse.transition.statespace.internal.SolutionStoreImpl; +import tools.refinery.store.map.Version; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.statecoding.StateCoderStoreAdapter; + +public class BestFirstStoreManager { + ModelStore modelStore; + ObjectivePriorityQueue objectiveStore; + ActivationStore activationStore; + SolutionStore solutionStore; + EquivalenceClassStore equivalenceClassStore; + + public BestFirstStoreManager(ModelStore modelStore) { + this.modelStore = modelStore; + DesignSpaceExplorationStoreAdapter storeAdapter = + modelStore.getAdapter(DesignSpaceExplorationStoreAdapter.class); + + objectiveStore = new ObjectivePriorityQueueImpl(storeAdapter.getObjectives()); + Procedure whenAllActivationsVisited = x -> objectiveStore.remove(x); + activationStore = new ActivationStoreImpl(storeAdapter.getTransformations().size(), whenAllActivationsVisited); + solutionStore = new SolutionStoreImpl(1); + equivalenceClassStore = new FastEquivalenceClassStore(modelStore.getAdapter(StateCoderStoreAdapter.class)) { + @Override + protected void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept) { + objectiveStore.submit(version); + activationStore.markNewAsVisited(version, emptyActivations); + if(accept) { + solutionStore.submit(version); + } + } + }; + } + + ObjectivePriorityQueue getObjectiveStore() { + return objectiveStore; + } + + ActivationStore getActivationStore() { + return activationStore; + } + + SolutionStore getSolutionStore() { + return solutionStore; + } + + EquivalenceClassStore getEquivalenceClassStore() { + return equivalenceClassStore; + } + + public void startExploration(Version initial) { + BestFirstExplorer bestFirstExplorer = new BestFirstExplorer(this, modelStore.createModelForState(initial), 1); + bestFirstExplorer.explore(); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java deleted file mode 100644 index 92d878ce..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstStrategy.java +++ /dev/null @@ -1,203 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Andras Szabolcs Nagy, Zoltan Ujhelyi and Daniel Varro - * Copyright (c) 2023 The Refinery Authors - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.store.dse.strategy; - -import tools.refinery.store.map.Version; -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; -import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.internal.Activation; -import tools.refinery.store.dse.objectives.Fitness; -import tools.refinery.store.dse.objectives.ObjectiveComparatorHelper; - -import java.util.*; - -public class BestFirstStrategy implements Strategy { - - private DesignSpaceExplorationAdapter dseAdapter; - - private int maxDepth = Integer.MAX_VALUE; - private int maxSolutions = Integer.MAX_VALUE; - private boolean backTrackIfSolution = true; - private boolean onlyBetterFirst = false; - - private PriorityQueue trajectoriesToExplore; - - private record TrajectoryWithFitness(List trajectory, Fitness fitness) { - @Override - public String toString() { - return trajectory.toString() + fitness.toString(); - } - - @Override - public int hashCode() { - return trajectory.get(trajectory.size() - 1).hashCode(); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof TrajectoryWithFitness other) { - return Objects.equals(trajectory.get(trajectory.size() - 1), other.trajectory.get(other.trajectory.size() - 1)); -// return trajectory.equals(((TrajectoryWithFitness) obj).trajectory); - } - return false; - } - } - - public BestFirstStrategy withDepthLimit(int maxDepth) { - if (maxDepth >= 0) { - this.maxDepth = maxDepth; - } - return this; - } - - public BestFirstStrategy withSolutionLimit(int maxSolutions) { - if (maxSolutions >= 0) { - this.maxSolutions = maxSolutions; - } - return this; - } - - public BestFirstStrategy continueIfHardObjectivesFulfilled() { - backTrackIfSolution = false; - return this; - } - - public BestFirstStrategy goOnOnlyIfFitnessIsBetter() { - onlyBetterFirst = true; - return this; - } - - @Override - public void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { - this.dseAdapter = designSpaceExplorationAdapter; - final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); - - trajectoriesToExplore = new PriorityQueue<>(11, - (o1, o2) -> objectiveComparatorHelper.compare(o2.fitness, o1.fitness)); - } - - @Override - public void explore() { - if (maxSolutions == 0) { - return; - } - final ObjectiveComparatorHelper objectiveComparatorHelper = dseAdapter.getObjectiveComparatorHelper(); - - boolean globalConstraintsAreSatisfied = dseAdapter.checkGlobalConstraints(); - if (!globalConstraintsAreSatisfied) { - // Global constraint is not satisfied in the first state. Terminate. - return; - } - - final Fitness firstFitness = dseAdapter.getFitness(); - if (firstFitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - // First state is a solution. Terminate. - if (backTrackIfSolution) { - return; - } - } - - if (maxDepth == 0) { - return; - } - - - var firstTrajectoryWithFitness = new TrajectoryWithFitness(dseAdapter.getTrajectory(), firstFitness); - trajectoriesToExplore.add(firstTrajectoryWithFitness); - TrajectoryWithFitness currentTrajectoryWithFitness = null; - - mainLoop: while (true) { - - if (currentTrajectoryWithFitness == null) { - if (trajectoriesToExplore.isEmpty()) { - // State space is fully traversed. - return; - } else { - currentTrajectoryWithFitness = trajectoriesToExplore.element(); - // New trajectory is chosen: " + currentTrajectoryWithFitness - dseAdapter.restoreTrajectory(currentTrajectoryWithFitness.trajectory); - } - } - - Collection activations = dseAdapter.getUntraversedActivations(); - Iterator iterator = activations.iterator(); - - - - while (iterator.hasNext()) { - final Activation nextActivation = iterator.next(); - if (!iterator.hasNext()) { - // Last untraversed activation of the state. - trajectoriesToExplore.remove(currentTrajectoryWithFitness); - } - - // Executing new activation - dseAdapter.fireActivation(nextActivation); - if (dseAdapter.isCurrentStateAlreadyTraversed()) { - // The new state is already visited. - dseAdapter.backtrack(); - } else if (!dseAdapter.checkGlobalConstraints()) { - // Global constraint is not satisfied. - dseAdapter.backtrack(); - } else { - final Fitness nextFitness = dseAdapter.getFitness(); - if (nextFitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - var solutions = dseAdapter.getSolutions().size(); - if (solutions >= maxSolutions) { - return; - } - // Found a solution. - if (backTrackIfSolution) { - dseAdapter.backtrack(); - continue; - } - } - if (dseAdapter.getDepth() >= maxDepth) { - // Reached max depth. - dseAdapter.backtrack(); - continue; - } - - TrajectoryWithFitness nextTrajectoryWithFitness = new TrajectoryWithFitness( - dseAdapter.getTrajectory(), nextFitness); - trajectoriesToExplore.add(nextTrajectoryWithFitness); - - int compare = objectiveComparatorHelper.compare(currentTrajectoryWithFitness.fitness, - nextTrajectoryWithFitness.fitness); - if (compare < 0) { - // Better fitness, moving on - currentTrajectoryWithFitness = nextTrajectoryWithFitness; - continue mainLoop; - } else if (compare == 0) { - if (onlyBetterFirst) { - // Equally good fitness, backtrack - dseAdapter.backtrack(); - } else { - // Equally good fitness, moving on - currentTrajectoryWithFitness = nextTrajectoryWithFitness; - continue mainLoop; - } - } else { - //"Worse fitness - currentTrajectoryWithFitness = null; - continue mainLoop; - } - } - } - - // State is fully traversed. - currentTrajectoryWithFitness = null; - - } - // Interrupted. - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java new file mode 100644 index 00000000..ea7fe43f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/BestFirstWorker.java @@ -0,0 +1,113 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.strategy; + +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; +import tools.refinery.store.dse.transition.ObjectiveValue; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.internal.ActivationStoreWorker; +import tools.refinery.store.map.Version; +import tools.refinery.store.model.Model; +import tools.refinery.store.statecoding.StateCoderAdapter; + + +import java.util.Random; + +public class BestFirstWorker { + final BestFirstStoreManager storeManager; + final Model model; + final ActivationStoreWorker activationStoreWorker; + final StateCoderAdapter stateCoderAdapter; + final DesignSpaceExplorationAdapter explorationAdapter; + + public BestFirstWorker(BestFirstStoreManager storeManager, Model model) { + this.storeManager = storeManager; + this.model = model; + + explorationAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); + stateCoderAdapter = model.getAdapter(StateCoderAdapter.class); + activationStoreWorker = new ActivationStoreWorker(storeManager.getActivationStore(), + explorationAdapter.getTransformations()); + } + + private VersionWithObjectiveValue last = null; + + //public boolean isIncluded + + public SubmitResult submit() { + if (explorationAdapter.checkExclude()) { + last = null; + return new SubmitResult(false, false, null, null); + } + + Version version = model.commit(); + ObjectiveValue objectiveValue = explorationAdapter.getObjectiveValue(); + var res = new VersionWithObjectiveValue(version, objectiveValue); + var code = stateCoderAdapter.calculateStateCode(); + var accepted = explorationAdapter.checkAccept(); + + boolean isNew = storeManager.getEquivalenceClassStore().submit(res, code, + activationStoreWorker.calculateEmptyActivationSize(), accepted); + + last = new VersionWithObjectiveValue(version, objectiveValue); + return new SubmitResult(isNew, accepted, objectiveValue, last); + } + + public void restoreToLast() { + if (explorationAdapter.getModel().hasUncommittedChanges()) { + explorationAdapter.getModel().restore(last.version()); + } + } + + public VersionWithObjectiveValue restoreToBest() { + var bestVersion = storeManager.getObjectiveStore().getBest(); + if (bestVersion != null) { + this.model.restore(bestVersion.version()); + } + return bestVersion; + } + + public VersionWithObjectiveValue restoreToRandom(Random random) { + var randomVersion = storeManager.getObjectiveStore().getRandom(random); + last = randomVersion; + if (randomVersion != null) { + this.model.restore(randomVersion.version()); + } + return randomVersion; + } + + public int compare(VersionWithObjectiveValue s1, VersionWithObjectiveValue s2) { + return storeManager.getObjectiveStore().getComparator().compare(s1, s2); + } + + public boolean stateHasUnvisited() { + if (!model.hasUncommittedChanges()) { + return storeManager.getActivationStore().hasUnmarkedActivation(last); + } else { + throw new IllegalStateException("The model has uncommitted changes!"); + } + } + + record RandomVisitResult(SubmitResult submitResult, boolean shouldRetry) { + } + + public RandomVisitResult visitRandomUnvisited(Random random) { + if (!model.hasUncommittedChanges()) { + var visitResult = activationStoreWorker.fireRandomActivation(this.last, random); + if (visitResult.successfulVisit()) { + return new RandomVisitResult(submit(), visitResult.mayHaveMore()); + } else { + return new RandomVisitResult(null, visitResult.mayHaveMore()); + } + } else { + throw new IllegalStateException("The model has uncommitted changes!"); + } + } + + public boolean hasEnoughSolution() { + return storeManager.solutionStore.hasEnoughSolution(); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java deleted file mode 100644 index 0a0caa7e..00000000 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/DepthFirstStrategy.java +++ /dev/null @@ -1,92 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.dse.strategy; - -import tools.refinery.store.dse.DesignSpaceExplorationAdapter; -import tools.refinery.store.dse.Strategy; -import tools.refinery.store.dse.objectives.Fitness; - -public class DepthFirstStrategy implements Strategy { - - private DesignSpaceExplorationAdapter dseAdapter; - - private int maxDepth = Integer.MAX_VALUE; - private int maxSolutions = Integer.MAX_VALUE; - private boolean backtrackFromSolution = true; - - public DepthFirstStrategy withDepthLimit(int maxDepth) { - if (maxDepth >= 0) { - this.maxDepth = maxDepth; - } - return this; - } - - public DepthFirstStrategy withSolutionLimit(int maxSolutions) { - if (maxSolutions >= 0) { - this.maxSolutions = maxSolutions; - } - return this; - } - - public DepthFirstStrategy continueIfHardObjectivesFulfilled() { - backtrackFromSolution = false; - return this; - } - - @Override - public void initialize(DesignSpaceExplorationAdapter designSpaceExplorationAdapter) { - this.dseAdapter = designSpaceExplorationAdapter; - } - - @Override - public void explore() { - if (maxSolutions == 0) { - return; - } - while (dseAdapter.getSolutions().size() < maxSolutions) { - if (!checkAndHandleGlobalConstraints()) { - return; - } - - Fitness fitness = dseAdapter.getFitness(); - if (fitness.isSatisfiesHardObjectives()) { - dseAdapter.newSolution(); - if (backtrackFromSolution && !dseAdapter.backtrack()) { - return; - } - } - - if (!checkAndHandleDepth()) { - return; - } - - if (!backtrackToLastUntraversed()) { - return; - } - - if (!dseAdapter.fireRandomActivation()) { - return; - } - } - } - - private boolean checkAndHandleGlobalConstraints() { - return dseAdapter.checkGlobalConstraints() || dseAdapter.backtrack(); - } - - private boolean checkAndHandleDepth() { - return dseAdapter.getDepth() < maxDepth || dseAdapter.backtrack(); - } - - private boolean backtrackToLastUntraversed() { - while (dseAdapter.getUntraversedActivations().isEmpty()) { - if (!dseAdapter.backtrack()) { - return false; - } - } - return true; - } -} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/SubmitResult.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/SubmitResult.java new file mode 100644 index 00000000..37d548d7 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/strategy/SubmitResult.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.strategy; + +import tools.refinery.store.dse.transition.ObjectiveValue; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.map.Version; + +public record SubmitResult(boolean include, boolean accepted, ObjectiveValue objective, VersionWithObjectiveValue newVersion) { + + } diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java new file mode 100644 index 00000000..37448309 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationAdapter.java @@ -0,0 +1,31 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.adapter.ModelAdapter; +import tools.refinery.store.dse.transition.internal.DesignSpaceExplorationBuilderImpl; +import tools.refinery.store.map.Version; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.store.tuple.Tuple1; + +import java.util.Collection; +import java.util.List; + +public interface DesignSpaceExplorationAdapter extends ModelAdapter { + + + + @Override + DesignSpaceExplorationStoreAdapter getStoreAdapter(); + + static DesignSpaceExplorationBuilder builder() { + return new DesignSpaceExplorationBuilderImpl(); + } + List getTransformations(); + boolean checkAccept(); + boolean checkExclude(); + ObjectiveValue getObjectiveValue(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java new file mode 100644 index 00000000..3855a20a --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationBuilder.java @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.adapter.ModelAdapterBuilder; +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.Objective; + +import java.util.Collection; +import java.util.List; + +public interface DesignSpaceExplorationBuilder extends ModelAdapterBuilder { + + DesignSpaceExplorationBuilder transformation(TransformationRule transformationRuleDefinition); + default DesignSpaceExplorationBuilder transformations(TransformationRule... transformationRuleDefinitions) { + return transformations(List.of(transformationRuleDefinitions)); + } + + default DesignSpaceExplorationBuilder transformations(Collection transformationRules) { + transformationRules.forEach(this::transformation); + return this; + } + + DesignSpaceExplorationBuilder accept(Criterion criteria); + + default DesignSpaceExplorationBuilder accept(Criterion... criteria) { + return accept(List.of(criteria)); + } + + default DesignSpaceExplorationBuilder accept(Collection criteria) { + criteria.forEach(this::accept); + return this; + } + + DesignSpaceExplorationBuilder exclude(Criterion criteria); + + default DesignSpaceExplorationBuilder exclude(Criterion... criteria) { + return exclude(List.of(criteria)); + } + + default DesignSpaceExplorationBuilder exclude(Collection criteria) { + criteria.forEach(this::exclude); + return this; + } + + DesignSpaceExplorationBuilder objective(Objective objective); + + default DesignSpaceExplorationBuilder objectives(Objective... objectives) { + return objectives(List.of(objectives)); + } + + default DesignSpaceExplorationBuilder objectives(Collection objectives) { + objectives.forEach(this::objective); + return this; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java new file mode 100644 index 00000000..5c8c7a4d --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/DesignSpaceExplorationStoreAdapter.java @@ -0,0 +1,27 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.adapter.ModelStoreAdapter; +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.model.Model; + +import java.util.List; + +public interface DesignSpaceExplorationStoreAdapter extends ModelStoreAdapter +{ + @Override + DesignSpaceExplorationAdapter createModelAdapter(Model model); + + List getTransformations(); + + List getAccepts(); + + List getExcludes(); + + List getObjectives(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValue.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValue.java new file mode 100644 index 00000000..89ee61c8 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValue.java @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +public interface ObjectiveValue { + double get(int index); + int getSize(); + + static ObjectiveValue of(double v1) { + return new ObjectiveValues.ObjectiveValue1(v1); + } + + static ObjectiveValue of(double v1, double v2) { + return new ObjectiveValues.ObjectiveValue2(v1,v2); + } + + static ObjectiveValue of(double[] v) { + return new ObjectiveValues.ObjectiveValueN(v); + } + +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValues.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValues.java new file mode 100644 index 00000000..60913ff3 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/ObjectiveValues.java @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import java.util.Arrays; + +public interface ObjectiveValues { + public record ObjectiveValue1(double value0) implements ObjectiveValue { + @Override + public double get(int index) { + if(index == 0) return value0; + else throw new IllegalArgumentException("No value at " + index); + } + + @Override + public int getSize() { + return 1; + } + } + public record ObjectiveValue2(double value0, double value1) implements ObjectiveValue { + @Override + public double get(int index) { + if(index == 0) return value0; + else if(index == 1) return value1; + else throw new IllegalArgumentException("No value at " + index); + } + + @Override + public int getSize() { + return 2; + } + } + public record ObjectiveValueN(double[] values) implements ObjectiveValue { + @Override + public double get(int index) { + return values[index]; + } + + @Override + public int getSize() { + return values().length; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ObjectiveValueN that = (ObjectiveValueN) o; + + return Arrays.equals(values, that.values); + } + + @Override + public int hashCode() { + return Arrays.hashCode(values); + } + + @Override + public String toString() { + return "ObjectiveValueN{" + + "values=" + Arrays.toString(values) + + '}'; + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java new file mode 100644 index 00000000..2cce738f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/Transformation.java @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import org.eclipse.collections.api.block.procedure.Procedure; +import tools.refinery.store.query.resultset.OrderedResultSet; +import tools.refinery.store.query.resultset.ResultSet; +import tools.refinery.store.tuple.Tuple; + +public class Transformation { + private final TransformationRule definition; + + private final OrderedResultSet activations; + + private final Procedure action; + + public Transformation(TransformationRule definition, OrderedResultSet activations, Procedure action) { + this.definition = definition; + this.activations = activations; + this.action = action; + } + + public TransformationRule getDefinition() { + return definition; + } + + public ResultSet getAllActivationsAsResultSet() { + return activations; + } + + public Tuple getActivation(int index) { + return activations.getKey(index); + } + + public boolean fireActivation(Tuple activation) { + action.accept(activation); + //queryEngine.flushChanges(); + return true; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java new file mode 100644 index 00000000..d64a3db1 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/TransformationRule.java @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.ModelQueryBuilder; +import tools.refinery.store.query.dnf.RelationalQuery; +import tools.refinery.store.dse.ActionFactory; +import tools.refinery.store.query.resultset.OrderedResultSet; +import tools.refinery.store.query.resultset.ResultSet; +import tools.refinery.store.tuple.Tuple; + +import java.util.*; + +public class TransformationRule { + + private final String name; + private final RelationalQuery precondition; + private final ActionFactory actionFactory; + + private Random random; + private ModelQueryAdapter queryEngine; + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory) { + this(name, precondition, actionFactory, new Random()); + } + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, long seed) { + this(name, precondition, actionFactory, new Random(seed)); + } + + public TransformationRule(String name, RelationalQuery precondition, ActionFactory actionFactory, Random random) { + this.name = name; + this.precondition = precondition; + this.actionFactory = actionFactory; + this.random = random; + } + public void doConfigure(ModelStoreBuilder storeBuilder) { + var queryBuilder = storeBuilder.getAdapter(ModelQueryBuilder.class); + queryBuilder.query(this.precondition); + } + + public Transformation prepare(Model model) { + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + var activations = new OrderedResultSet<>(queryEngine.getResultSet(precondition)); + var action = actionFactory.prepare(model); + return new Transformation(this,activations,action); + } + + public String getName() { + return name; + } + + public RelationalQuery getPrecondition() { + return precondition; + } + +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/VersionWithObjectiveValue.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/VersionWithObjectiveValue.java new file mode 100644 index 00000000..ca28e27f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/VersionWithObjectiveValue.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition; + +import tools.refinery.store.map.Version; + +public record VersionWithObjectiveValue(Version version, ObjectiveValue objectiveValue) { +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java new file mode 100644 index 00000000..e1a29d40 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationAdapterImpl.java @@ -0,0 +1,90 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.internal; + +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; +import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter; +import tools.refinery.store.dse.transition.ObjectiveValue; +import tools.refinery.store.dse.transition.Transformation; +import tools.refinery.store.dse.transition.objectives.CriterionCalculator; +import tools.refinery.store.dse.transition.objectives.ObjectiveCalculator; +import tools.refinery.store.model.Model; + +import java.util.List; + +public class DesignSpaceExplorationAdapterImpl implements DesignSpaceExplorationAdapter { + final Model model; + final DesignSpaceExplorationStoreAdapter designSpaceExplorationStoreAdapter; + + final List transformations; + final List accepts; + final List excludes; + final List objectives; + + public DesignSpaceExplorationAdapterImpl(Model model, + DesignSpaceExplorationStoreAdapter designSpaceExplorationStoreAdapter, + List transformations, + List accepts, + List excludes, + List objectives) { + this.model = model; + this.designSpaceExplorationStoreAdapter = designSpaceExplorationStoreAdapter; + + this.transformations = transformations; + this.accepts = accepts; + this.excludes = excludes; + this.objectives = objectives; + } + + @Override + public Model getModel() { + return model; + } + + @Override + public DesignSpaceExplorationStoreAdapter getStoreAdapter() { + return designSpaceExplorationStoreAdapter; + } + + public List getTransformations() { + return transformations; + } + + @Override + public boolean checkAccept() { + for (var accept : this.accepts) { + if (!accept.isSatisfied()) { + return false; + } + } + return true; + } + + @Override + public boolean checkExclude() { + for (var exclude : this.excludes) { + if (exclude.isSatisfied()) { + return true; + } + } + return false; + } + + @Override + public ObjectiveValue getObjectiveValue() { + if (objectives.size() == 1) { + return ObjectiveValue.of(objectives.get(0).getValue()); + } else if (objectives.size() == 2) { + return ObjectiveValue.of(objectives.get(0).getValue(), objectives.get(1).getValue()); + } else { + double[] res = new double[objectives.size()]; + for (int i = 0; i < objectives.size(); i++) { + res[i] = objectives.get(i).getValue(); + } + return ObjectiveValue.of(res); + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java new file mode 100644 index 00000000..4371cc03 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationBuilderImpl.java @@ -0,0 +1,75 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.internal; + +import tools.refinery.store.adapter.AbstractModelAdapterBuilder; +import tools.refinery.store.dse.transition.DesignSpaceExplorationBuilder; +import tools.refinery.store.dse.transition.TransformationRule; +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.model.ModelStoreBuilder; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; + +public class DesignSpaceExplorationBuilderImpl + extends AbstractModelAdapterBuilder + implements DesignSpaceExplorationBuilder { + + LinkedHashSet transformationRuleDefinitions = new LinkedHashSet<>(); + LinkedHashSet accepts = new LinkedHashSet<>(); + LinkedHashSet excludes = new LinkedHashSet<>(); + LinkedHashSet objectives = new LinkedHashSet<>(); + + @Override + public DesignSpaceExplorationBuilder transformation(TransformationRule transformationRuleDefinition) { + transformationRuleDefinitions.add(transformationRuleDefinition); + return this; + } + + @Override + public DesignSpaceExplorationBuilder accept(Criterion criteria) { + accepts.add(criteria); + return this; + } + + @Override + public DesignSpaceExplorationBuilder exclude(Criterion criteria) { + excludes.add(criteria); + return this; + } + + + @Override + public DesignSpaceExplorationBuilder objective(Objective objective) { + objectives.add(objective); + return this; + } + + @Override + protected void doConfigure(ModelStoreBuilder storeBuilder) { + transformationRuleDefinitions.forEach(x -> x.doConfigure(storeBuilder)); + accepts.forEach(x -> x.doConfigure(storeBuilder)); + excludes.forEach(x -> x.doConfigure(storeBuilder)); + objectives.forEach(x -> x.doConfigure(storeBuilder)); + + super.doConfigure(storeBuilder); + } + + @Override + protected DesignSpaceExplorationStoreAdapterImpl doBuild(ModelStore store) { + List transformationRuleDefinitiions1 = new ArrayList<>(transformationRuleDefinitions); + List accepts1 = new ArrayList<>(accepts); + List excludes1 = new ArrayList<>(excludes); + List objectives1 = new ArrayList<>(objectives); + + return new DesignSpaceExplorationStoreAdapterImpl(store, + transformationRuleDefinitiions1, accepts1, + excludes1, objectives1); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java new file mode 100644 index 00000000..3319e148 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/internal/DesignSpaceExplorationStoreAdapterImpl.java @@ -0,0 +1,70 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.internal; + +import tools.refinery.store.dse.transition.DesignSpaceExplorationStoreAdapter; +import tools.refinery.store.dse.transition.Transformation; +import tools.refinery.store.dse.transition.TransformationRule; +import tools.refinery.store.dse.transition.objectives.Criterion; +import tools.refinery.store.dse.transition.objectives.CriterionCalculator; +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.dse.transition.objectives.ObjectiveCalculator; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; + +import java.util.List; + +public class DesignSpaceExplorationStoreAdapterImpl implements DesignSpaceExplorationStoreAdapter { + protected final ModelStore store; + + protected final List transformationRuleDefinitions; + protected final List accepts; + protected final List excludes; + protected final List objectives; + + public DesignSpaceExplorationStoreAdapterImpl(ModelStore store, + List transformationRuleDefinitions, + List accepts, List excludes, + List objectives) { + this.store = store; + + this.transformationRuleDefinitions = transformationRuleDefinitions; + this.accepts = accepts; + this.excludes = excludes; + this.objectives = objectives; + } + + @Override + public ModelStore getStore() { + return store; + } + + @Override + public DesignSpaceExplorationAdapterImpl createModelAdapter(Model model) { + final List t = this.transformationRuleDefinitions.stream().map(x->x.prepare(model)).toList(); + final List a = this.accepts.stream().map(x->x.createCalculator(model)).toList(); + final List e = this.excludes.stream().map(x->x.createCalculator(model)).toList(); + final List o = this.objectives.stream().map(x->x.createCalculator(model)).toList(); + + return new DesignSpaceExplorationAdapterImpl(model, this, t, a, e, o); + } + @Override + public List getTransformations() { + return transformationRuleDefinitions; + } + @Override + public List getAccepts() { + return accepts; + } + @Override + public List getExcludes() { + return excludes; + } + @Override + public List getObjectives() { + return objectives; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java new file mode 100644 index 00000000..66ca6f5e --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Criterion.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; + +public interface Criterion { + default void doConfigure(ModelStoreBuilder storeBuilder) { + } + CriterionCalculator createCalculator(Model model); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CriterionCalculator.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CriterionCalculator.java new file mode 100644 index 00000000..944ffed6 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/CriterionCalculator.java @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +public interface CriterionCalculator { + boolean isSatisfied(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java new file mode 100644 index 00000000..b5924455 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/Objective.java @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; + +public interface Objective { + default void doConfigure(ModelStoreBuilder storeBuilder) { + } + ObjectiveCalculator createCalculator(Model model); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/ObjectiveCalculator.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/ObjectiveCalculator.java new file mode 100644 index 00000000..f01b8de9 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/ObjectiveCalculator.java @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +public interface ObjectiveCalculator { + double getValue(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java new file mode 100644 index 00000000..e2260cca --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryCriteria.java @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.ModelQueryBuilder; +import tools.refinery.store.query.dnf.AnyQuery; + +public class QueryCriteria implements Criterion { + protected final boolean acceptIfHasMatch; + protected final AnyQuery query; + + /** + * Criteria based on the existence of matches evaluated on the model. + * @param query The query evaluated on the model. + * @param acceptIfHasMatch If true, the criteria satisfied if the query has any match on the model. Otherwise, + * the criteria satisfied if the query has no match on the model. + */ + public QueryCriteria(AnyQuery query, boolean acceptIfHasMatch) { + this.query = query; + this.acceptIfHasMatch = acceptIfHasMatch; + } + + @Override + public CriterionCalculator createCalculator(Model model) { + var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(query); + if(acceptIfHasMatch) { + return () -> resultSet.size() > 0; + } else { + return () -> resultSet.size() == 0; + } + } + + @Override + public void doConfigure(ModelStoreBuilder storeBuilder) { + Criterion.super.doConfigure(storeBuilder); + storeBuilder.getAdapter(ModelQueryBuilder.class).query(query); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java new file mode 100644 index 00000000..dfddccfc --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/objectives/QueryObjective.java @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.objectives; + +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.query.ModelQueryBuilder; +import tools.refinery.store.query.dnf.FunctionalQuery; + +public class QueryObjective implements Objective { + protected final FunctionalQuery objectiveFunction; + + public QueryObjective(FunctionalQuery objectiveFunction) { + this.objectiveFunction = objectiveFunction; + } + + @Override + public ObjectiveCalculator createCalculator(Model model) { + var resultSet = model.getAdapter(ModelQueryAdapter.class).getResultSet(objectiveFunction); + return () -> { + var cursor = resultSet.getAll(); + boolean hasElement = cursor.move(); + if(hasElement) { + double result = cursor.getValue().doubleValue(); + if(cursor.move()) { + throw new IllegalStateException("Query providing the objective function has multiple values!"); + } + return result; + } else { + throw new IllegalStateException("Query providing the objective function has no values!"); + } + }; + } + + @Override + public void doConfigure(ModelStoreBuilder storeBuilder) { + Objective.super.doConfigure(storeBuilder); + storeBuilder.getAdapter(ModelQueryBuilder.class).query(objectiveFunction); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ActivationStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ActivationStore.java new file mode 100644 index 00000000..52e0611d --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ActivationStore.java @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.map.Version; + +import java.util.Random; + +public interface ActivationStore { + record VisitResult(boolean successfulVisit, boolean mayHaveMore, int transformation, int activation) { } + VisitResult markNewAsVisited(VersionWithObjectiveValue to, int[] emptyEntrySizes); + boolean hasUnmarkedActivation(VersionWithObjectiveValue version); + VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java new file mode 100644 index 00000000..bbe26fe5 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/EquivalenceClassStore.java @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.statecoding.StateCoderResult; + +public interface EquivalenceClassStore { + boolean submit(VersionWithObjectiveValue version, StateCoderResult stateCoderResult, int[] emptyActivations, boolean accept); + boolean hasUnresolvedSymmetry(); + void resolveOneSymmetry(); + int getNumberOfUnresolvedSymmetries(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ObjectivePriorityQueue.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ObjectivePriorityQueue.java new file mode 100644 index 00000000..df72c343 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/ObjectivePriorityQueue.java @@ -0,0 +1,21 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.map.Version; + +import java.util.Comparator; +import java.util.Random; + +public interface ObjectivePriorityQueue { + Comparator getComparator(); + void submit(VersionWithObjectiveValue versionWithObjectiveValue); + void remove(VersionWithObjectiveValue versionWithObjectiveValue); + int getSize(); + VersionWithObjectiveValue getBest(); + VersionWithObjectiveValue getRandom(Random random); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/SolutionStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/SolutionStore.java new file mode 100644 index 00000000..d1bfaa79 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/SolutionStore.java @@ -0,0 +1,17 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; + +import java.util.List; +import java.util.concurrent.Future; + +public interface SolutionStore { + boolean submit(VersionWithObjectiveValue version); + List getSolutions(); + boolean hasEnoughSolution(); +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java new file mode 100644 index 00000000..8466a0f3 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/AbstractEquivalenceClassStore.java @@ -0,0 +1,47 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore; +import tools.refinery.store.statecoding.StateCoderResult; +import tools.refinery.store.statecoding.StateCoderStoreAdapter; + +public abstract class AbstractEquivalenceClassStore implements EquivalenceClassStore { + protected final StateCoderStoreAdapter stateCoderStoreAdapter; + AbstractEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) { + this.stateCoderStoreAdapter = stateCoderStoreAdapter; + } + + protected int numberOfUnresolvedSymmetries = 0; + + protected abstract void delegate(VersionWithObjectiveValue version, int[] emptyActivations, boolean accept); + protected abstract boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion, + int[] emptyActivations, boolean accept); + + @Override + public synchronized boolean submit(VersionWithObjectiveValue version, StateCoderResult stateCoderResult, + int[] emptyActivations, boolean accept) { + boolean hasNewVersion = tryToAdd(stateCoderResult, version, emptyActivations, accept); + if (hasNewVersion) { + delegate(version, emptyActivations, accept); + return true; + } else { + numberOfUnresolvedSymmetries++; + return false; + } + } + + @Override + public boolean hasUnresolvedSymmetry() { + return numberOfUnresolvedSymmetries > 0; + } + + @Override + public int getNumberOfUnresolvedSymmetries() { + return numberOfUnresolvedSymmetries; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java new file mode 100644 index 00000000..ba243d7d --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreBitVectorEntry.java @@ -0,0 +1,46 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +public class ActivationStoreBitVectorEntry extends ActivationStoreEntry { + final int[] selected; + + ActivationStoreBitVectorEntry(int numberOfActivations) { + super(numberOfActivations); + this.selected = new int[(numberOfActivations / Integer.SIZE) + 1]; + } + + @Override + public int getNumberOfUnvisitedActivations() { + int visited = 0; + for (int i : selected) { + visited += Integer.bitCount(i); + } + return visited; + } + + private static final int ELEMENT_POSITION = 5; // size of Integer.SIZE + private static final int ELEMENT_BITMASK = (1<> ELEMENT_POSITION; + final int selectedBit = position & ELEMENT_BITMASK; + if((selected[selectedElement] & selectedBit) == 0) { + selected[selectedElement] |= selectedBit; + return position; + } else { + if(position < this.numberOfActivations) { + position++; + } else { + position = 0; + } + } + } while(position != index); + throw new IllegalArgumentException("There is are no unvisited activations!"); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java new file mode 100644 index 00000000..f69b234c --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreEntry.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +public abstract class ActivationStoreEntry { + protected final int numberOfActivations; + + ActivationStoreEntry(int numberOfActivations) { + this.numberOfActivations = numberOfActivations; + } + + public int getNumberOfVisitedActivations() { + return numberOfActivations; + } + + public abstract int getNumberOfUnvisitedActivations(); + public abstract int getAndAddActivationAfter(int index); + + // public abstract boolean contains(int activation) + // public abstract boolean add(int activation) + + public static ActivationStoreEntry create(int size) { + if(size <= Integer.SIZE*6) { + return new ActivationStoreBitVectorEntry(size); + } else { + return new ActivationStoreListEntry(size); + } + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java new file mode 100644 index 00000000..1229ec15 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreImpl.java @@ -0,0 +1,131 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import org.eclipse.collections.api.block.procedure.Procedure; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.ActivationStore; + +import java.util.*; + +public class ActivationStoreImpl implements ActivationStore { + final int numberOfTransformations; + final Procedure actionWhenAllActivationVisited; + final Map> versionToActivations; + + public ActivationStoreImpl(final int numberOfTransformations, + Procedure actionWhenAllActivationVisited) { + this.numberOfTransformations = numberOfTransformations; + this.actionWhenAllActivationVisited = actionWhenAllActivationVisited; + versionToActivations = new HashMap<>(); + } + + public synchronized VisitResult markNewAsVisited(VersionWithObjectiveValue to, int[] emptyEntrySizes) { + boolean[] successful = new boolean[]{false}; + var entries = versionToActivations.computeIfAbsent(to, x -> { + successful[0] = true; + List result = new ArrayList<>(emptyEntrySizes.length); + for(int emptyEntrySize : emptyEntrySizes) { + result.add(ActivationStoreListEntry.create(emptyEntrySize)); + } + return result; + }); + boolean hasMore = false; + for (var entry : entries) { + if (entry.getNumberOfUnvisitedActivations() > 0) { + hasMore = true; + break; + } + } + if(!hasMore) { + actionWhenAllActivationVisited.accept(to); + } + return new VisitResult(successful[0], hasMore, -1, -1); + } + + public synchronized VisitResult visitActivation(VersionWithObjectiveValue from, int transformationIndex, int activationIndex) { + var entries = versionToActivations.get(from); + var entry = entries.get(transformationIndex); + final int unvisited = entry.getNumberOfUnvisitedActivations(); + + final boolean successfulVisit = unvisited > 0; + final boolean hasMoreInActivation = unvisited > 1; + final boolean hasMore; + final int transformation; + final int activation; + + if (successfulVisit) { + transformation = transformationIndex; + activation = entry.getAndAddActivationAfter(activationIndex); + + } else { + transformation = -1; + activation = -1; + } + + if(hasMoreInActivation) { + boolean hasMoreInOtherTransformation = false; + for (var e : entries) { + if (e != entry && e.getNumberOfVisitedActivations() > 0) { + hasMoreInOtherTransformation = true; + break; + } + } + hasMore = hasMoreInOtherTransformation; + } else { + hasMore = true; + } + + if(!hasMore) { + actionWhenAllActivationVisited.accept(from); + } + + return new VisitResult(false, hasMore, transformation, activation); + } + + @Override + public synchronized boolean hasUnmarkedActivation(VersionWithObjectiveValue version) { + var entries = versionToActivations.get(version); + boolean hasMore = false; + for (var entry : entries) { + if (entry.getNumberOfUnvisitedActivations() > 0) { + hasMore = true; + break; + } + } + return hasMore; + } + + @Override + public synchronized VisitResult getRandomAndMarkAsVisited(VersionWithObjectiveValue version, Random random) { + var entries = versionToActivations.get(version); + + int sum1 = 0; + for (var entry : entries) { + sum1 += entry.getNumberOfUnvisitedActivations(); + } + + int selected = random.nextInt(sum1); + int sum2 = 0; + int transformation = 0; + int activation = -1; + for (; transformation < entries.size(); transformation++) { + var entry = entries.get(transformation); + int unvisited = entry.getNumberOfUnvisitedActivations(); + if (selected < sum2 + unvisited) { + activation = sum2 + unvisited - selected; + break; + } else { + sum2 += unvisited; + } + } + if (activation == -1) { + throw new IllegalArgumentException("no unvisited"); + } + + return this.visitActivation(version, transformation, activation); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java new file mode 100644 index 00000000..9e25301f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreListEntry.java @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import org.eclipse.collections.impl.list.mutable.primitive.IntArrayList; + +public class ActivationStoreListEntry extends ActivationStoreEntry { + IntArrayList visitedActivations; + + ActivationStoreListEntry(int numberOfActivations) { + super(numberOfActivations); + visitedActivations = new IntArrayList(); + } + + @Override + public int getNumberOfUnvisitedActivations() { + return this.numberOfActivations - visitedActivations.size(); + } + + @Override + public int getAndAddActivationAfter(int index) { + // If it is empty, just add it. + if(this.visitedActivations.isEmpty()) { + this.visitedActivations.add(index); + return index; + } + + int position = getPosition(index); + + // If the index is not in the position, one can insert it + if(this.visitedActivations.get(position) != index) { + this.visitedActivations.addAtIndex(position,index); + return index; + } + + // Otherwise, get the next empty space between two elements + while(position + 2 < this.visitedActivations.size()) { + position++; + if(this.visitedActivations.get(position+1)-this.visitedActivations.get(position) > 1) { + this.visitedActivations.addAtIndex(position+1, this.visitedActivations.get(position+1)+1); + } + } + + // Otherwise, try to add to the last space + int last = this.visitedActivations.get(this.visitedActivations.size()-1); + if(last 0) { + this.visitedActivations.add(0); + return 0; + } + + // Otherwise, get the next empty space between two elements + position = 0; + while(position + 2 < this.visitedActivations.size()) { + position++; + if(this.visitedActivations.get(position+1)-this.visitedActivations.get(position) > 1) { + this.visitedActivations.addAtIndex(position+1, this.visitedActivations.get(position+1)+1); + } + } + + throw new IllegalArgumentException("There is are no unvisited activations!"); + } + + /** + * Returns the position of an index in the {@code visitedActivations}. If the collection contains the index, in + * returns its position, otherwise, it returns the position where the index need to be put. + * + * @param index Index of an activation. + * @return The position of the index. + */ + private int getPosition(int index) { + int left = 0; + int right = this.visitedActivations.size() - 1; + while (left <= right) { + final int middle = (right - left) / 2 + left; + final int middleElement = visitedActivations.get(middle); + if(middleElement == index) { + return middle; + } else if(middleElement < index) { + left = middle +1; + } else{ + right = middle-1; + } + } + return right+1; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java new file mode 100644 index 00000000..e05f5122 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ActivationStoreWorker.java @@ -0,0 +1,56 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import tools.refinery.store.dse.transition.Transformation; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.ActivationStore; +import tools.refinery.store.map.Version; + +import java.util.List; +import java.util.Random; + +public class ActivationStoreWorker { + final ActivationStore store; + final List transformations; + + public ActivationStoreWorker(ActivationStore store, List transformations) { + this.store = store; + this.transformations = transformations; + } + + public int[] calculateEmptyActivationSize() { + int[] result = new int[transformations.size()]; + for (int i = 0; i < result.length; i++) { + result[i] = transformations.get(i).getAllActivationsAsResultSet().size(); + } + return result; + } + + + public ActivationStore.VisitResult fireRandomActivation(VersionWithObjectiveValue thisVersion, Random random) { + var result = store.getRandomAndMarkAsVisited(thisVersion, random); + if(result.successfulVisit()) { + int selectedTransformation = result.transformation(); + int selectedActivation = result.activation(); + + Transformation transformation = transformations.get(selectedTransformation); + var tuple = transformation.getActivation(selectedActivation); + + boolean success = transformation.fireActivation(tuple); + if(success) { + return result; + } else { + return new ActivationStore.VisitResult( + false, + result.mayHaveMore(), + selectedActivation, + selectedActivation); + } + } + return result; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java new file mode 100644 index 00000000..1555c3be --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java @@ -0,0 +1,107 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.ActivationStore; +import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue; +import tools.refinery.store.dse.transition.statespace.SolutionStore; +import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore; +import tools.refinery.store.statecoding.StateCoderResult; +import tools.refinery.store.statecoding.StateCoderStoreAdapter; +import tools.refinery.store.statecoding.StateEquivalenceChecker; + +import java.util.ArrayList; + +public abstract class CompleteEquivalenceClassStore extends AbstractEquivalenceClassStore implements EquivalenceClassStore { + + static class SymmetryStoreArray extends ArrayList { + final int[] activationSizes; + final boolean accept; + + SymmetryStoreArray(int[] activationSizes, boolean accept) { + super(); + this.activationSizes = activationSizes; + this.accept = accept; + } + } + + final IntObjectHashMap modelCode2Versions; + + protected CompleteEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) { + super(stateCoderStoreAdapter); + this.modelCode2Versions = new IntObjectHashMap<>(); + } + + @Override + protected boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion, + int[] emptyActivations, boolean accept) { + int modelCode = stateCoderResult.modelCode(); + Object old = modelCode2Versions.updateValue( + modelCode, + () -> newVersion, + x -> { + if (x instanceof SymmetryStoreArray array) { + if(array.accept != accept || array.activationSizes != emptyActivations) { + this.delegate(newVersion,emptyActivations,accept); + return x; + } + array.add(newVersion); + return array; + } else { + SymmetryStoreArray result = new SymmetryStoreArray(emptyActivations, accept); + result.add((VersionWithObjectiveValue) x); + result.add(newVersion); + return result; + } + }); + return old == null; + } + + @Override + public void resolveOneSymmetry() { + var unresolvedSimilarity = getOneUnresolvedSymmetry(); + if (unresolvedSimilarity == null) { + return; + } + var outcome = this.stateCoderStoreAdapter.checkEquivalence(unresolvedSimilarity.get(0), + unresolvedSimilarity.get(1)); + if (outcome != StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC) { + delegate(unresolvedSimilarity.get(1), unresolvedSimilarity.activationSizes, unresolvedSimilarity.accept); + } + } + + //record UnresolvedSymmetryResult + + private synchronized SymmetryStoreArray getOneUnresolvedSymmetry() { + if (numberOfUnresolvedSymmetries <= 0) { + return null; + } + + for (var entry : modelCode2Versions.keyValuesView()) { + int hash = entry.getOne(); + var value = entry.getTwo(); + if (value instanceof SymmetryStoreArray array) { + int size = array.size(); + var representative = array.get(0); + var similar = array.get(size - 1); + array.remove(size - 1); + + if (size <= 2) { + modelCode2Versions.put(hash, representative); + } + + var result = new SymmetryStoreArray(array.activationSizes, array.accept); + result.add(representative); + result.add(similar); + return result; + } + } + + return null; + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java new file mode 100644 index 00000000..75e117c0 --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/FastEquivalenceClassStore.java @@ -0,0 +1,32 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore; +import tools.refinery.store.statecoding.StateCoderResult; +import tools.refinery.store.statecoding.StateCoderStoreAdapter; + +public abstract class FastEquivalenceClassStore extends AbstractEquivalenceClassStore implements EquivalenceClassStore { + + final IntHashSet codes; + + public FastEquivalenceClassStore(StateCoderStoreAdapter stateCoderStoreAdapter) { + super(stateCoderStoreAdapter); + this.codes = new IntHashSet(); + } + + @Override + protected boolean tryToAdd(StateCoderResult stateCoderResult, VersionWithObjectiveValue newVersion, int[] emptyActivations, boolean accept) { + return this.codes.add(stateCoderResult.modelCode()); + } + + @Override + public void resolveOneSymmetry() { + throw new IllegalArgumentException("This equivalence storage is not prepared to resolve symmetries!"); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java new file mode 100644 index 00000000..249b22da --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/ObjectivePriorityQueueImpl.java @@ -0,0 +1,74 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import tools.refinery.store.dse.transition.ObjectiveValues; +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.objectives.Objective; +import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue; +import tools.refinery.store.map.Version; + +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; +import java.util.Random; + +public class ObjectivePriorityQueueImpl implements ObjectivePriorityQueue { + public static final Comparator c1 = (o1, o2) -> Double.compare( + ((ObjectiveValues.ObjectiveValue1) o1.objectiveValue()).value0(), + ((ObjectiveValues.ObjectiveValue1) o2.objectiveValue()).value0()); + // TODO: support multi objective! + final PriorityQueue priorityQueue; + + public ObjectivePriorityQueueImpl(List objectives) { + + if(objectives.size() == 1) { + this.priorityQueue = new PriorityQueue<>(c1); + } else { + throw new UnsupportedOperationException("Only single objective comparator is implemented currently!"); + } + } + @Override + public Comparator getComparator() { + return c1; + } + + @Override + public void submit(VersionWithObjectiveValue versionWithObjectiveValue) { + priorityQueue.add(versionWithObjectiveValue); + } + + @Override + public void remove(VersionWithObjectiveValue versionWithObjectiveValue) { + priorityQueue.remove(versionWithObjectiveValue); + } + + @Override + public int getSize() { + return priorityQueue.size(); + } + + @Override + public VersionWithObjectiveValue getBest() { + var best = priorityQueue.peek(); + if (best != null) { + return best; + } else { + throw new IllegalArgumentException("The objective store is empty!"); + } + } + + @Override + public VersionWithObjectiveValue getRandom(Random random) { + int randomPosition = random.nextInt(getSize()); + for (VersionWithObjectiveValue entry : this.priorityQueue) { + if (randomPosition-- == 0) { + return entry; + } + } + throw new IllegalStateException("The priority queue is inconsistent!"); + } +} diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java new file mode 100644 index 00000000..cc48864f --- /dev/null +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/SolutionStoreImpl.java @@ -0,0 +1,53 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import tools.refinery.store.dse.transition.VersionWithObjectiveValue; +import tools.refinery.store.dse.transition.statespace.SolutionStore; + +import java.util.ArrayList; +import java.util.List; +import java.util.SortedSet; +import java.util.TreeSet; + +public class SolutionStoreImpl implements SolutionStore { + final int maxNumberSolutions; + public static final int UNLIMITED = -1; + final SortedSet solutions; + + public SolutionStoreImpl(int maxNumberSolutions) { + this.maxNumberSolutions = maxNumberSolutions; + solutions = new TreeSet<>(ObjectivePriorityQueueImpl.c1); + } + + + @Override + public synchronized boolean submit(VersionWithObjectiveValue version) { + boolean removeLast = hasEnoughSolution(); + solutions.add(version); + if(removeLast) { + var last = solutions.last(); + solutions.remove(last); + return last != version; + } else { + return true; + } + } + + @Override + public List getSolutions() { + return new ArrayList<>(solutions); + } + + @Override + public boolean hasEnoughSolution() { + if (maxNumberSolutions == UNLIMITED) { + return false; + } else { + return solutions.size() >= maxNumberSolutions; + } + } +} diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index 225de32e..b0b416e1 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -8,13 +8,13 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.dse.strategy.BestFirstStrategy; -import tools.refinery.store.dse.strategy.DepthFirstStrategy; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; @@ -282,5 +282,5 @@ class CRAExamplesTest { var states = dseAdapter.explore(); System.out.println("states size: " + states.size()); } - +*/ } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index c6da896c..461d9353 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -8,11 +8,11 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.internal.TransformationRule; -import tools.refinery.store.dse.strategy.BestFirstStrategy; +import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.dse.strategy.DepthFirstStrategy; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java index 91e33f4a..083f7ec3 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java @@ -6,10 +6,11 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.dse.strategy.BestFirstStrategy; import tools.refinery.store.dse.strategy.DepthFirstStrategy; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java index 5d24d712..1672b63a 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java @@ -7,10 +7,11 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; import tools.refinery.store.dse.strategy.DepthFirstStrategy; +import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.internal.TransformationRule; +import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java new file mode 100644 index 00000000..e7960a06 --- /dev/null +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/transition/statespace/internal/ActivationUnitTest.java @@ -0,0 +1,72 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.dse.transition.statespace.internal; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +class ActivationUnitTest { + private final static int SMALL_SIZE = 5; + + private static Stream entries() { + return Stream.of( + new ActivationStoreBitVectorEntry(SMALL_SIZE), + new ActivationStoreListEntry(SMALL_SIZE)); + } + + void addTest(ActivationStoreEntry entry, int elementsAdded) { + Assertions.assertEquals(elementsAdded,entry.getNumberOfVisitedActivations()); + Assertions.assertEquals(SMALL_SIZE-elementsAdded,entry.getNumberOfUnvisitedActivations()); + } + + @ParameterizedTest + @MethodSource("entries") + void testDifferent(ActivationStoreEntry entry) { + int elementsAdded = 0; + addTest(entry,elementsAdded); + Assertions.assertEquals(2, entry.getAndAddActivationAfter(2)); + addTest(entry,++elementsAdded); + Assertions.assertEquals(3,entry.getAndAddActivationAfter(3)); + addTest(entry,++elementsAdded); + Assertions.assertEquals(1,entry.getAndAddActivationAfter(1)); + addTest(entry,++elementsAdded); + Assertions.assertEquals(4,entry.getAndAddActivationAfter(4)); + addTest(entry,++elementsAdded); + Assertions.assertEquals(0,entry.getAndAddActivationAfter(0)); + addTest(entry,++elementsAdded); + } + + @ParameterizedTest + @MethodSource("entries") + void testSame(ActivationStoreEntry entry) { + int elementsAdded = 0; + addTest(entry,elementsAdded); + entry.getAndAddActivationAfter(2); + addTest(entry,++elementsAdded); + entry.getAndAddActivationAfter(2); + addTest(entry,++elementsAdded); + entry.getAndAddActivationAfter(2); + addTest(entry,++elementsAdded); + entry.getAndAddActivationAfter(2); + addTest(entry,++elementsAdded); + entry.getAndAddActivationAfter(2); + addTest(entry,++elementsAdded); + } + + @ParameterizedTest + @MethodSource("entries") + void testFilling(ActivationStoreEntry entry) { + int elementsAdded = 0; + while(elementsAdded < SMALL_SIZE) { + entry.getAndAddActivationAfter(2); + elementsAdded++; + } + Assertions.assertThrows(IllegalArgumentException.class,()-> entry.getAndAddActivationAfter(2)); + } +} -- cgit v1.2.3-54-g00ecf From 97e06c7d8c2cb4b15f5011e381f0b33fcd16a10c Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 5 Sep 2023 07:59:17 +0200 Subject: restructured DSE framework, failing build --- .../tools/refinery/store/model/internal/ModelVersion.java | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'subprojects') 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,27 +11,15 @@ import java.util.Arrays; public class ModelVersion implements Version { final Version[] mapVersions; - final int hash; public ModelVersion(Version[] mapVersions) { this.mapVersions = mapVersions; - this.hash = Arrays.hashCode(mapVersions); } public static Version getInternalVersion(Version modelVersion, int interpretationIndex) { return ((ModelVersion) modelVersion).mapVersions[interpretationIndex]; } - @Override - public int hashCode() { - return hash; - } - - @Override - public boolean equals(Object obj) { - return super.equals(obj); - } - @Override public String toString() { return "ModelVersion{" + -- cgit v1.2.3-54-g00ecf From a00a2a961accd8ebbe34c3c1e50b6fa782e86ca4 Mon Sep 17 00:00:00 2001 From: OszkarSemerath Date: Tue, 5 Sep 2023 07:59:17 +0200 Subject: restructured DSE framework, failing build --- .../internal/CompleteEquivalenceClassStore.java | 7 +- .../tools/refinery/store/dse/CRAExamplesTest.java | 289 +++-- .../java/tools/refinery/store/dse/DebugTest.java | 181 ++-- .../store/dse/DesignSpaceExplorationTest.java | 1128 ++++++++++---------- .../refinery/store/dse/TransformationRuleTest.java | 808 +++++++------- 5 files changed, 1188 insertions(+), 1225 deletions(-) (limited to 'subprojects') diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java index 1555c3be..925e09a3 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/statespace/internal/CompleteEquivalenceClassStore.java @@ -7,9 +7,6 @@ package tools.refinery.store.dse.transition.statespace.internal; import org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap; import tools.refinery.store.dse.transition.VersionWithObjectiveValue; -import tools.refinery.store.dse.transition.statespace.ActivationStore; -import tools.refinery.store.dse.transition.statespace.ObjectivePriorityQueue; -import tools.refinery.store.dse.transition.statespace.SolutionStore; import tools.refinery.store.dse.transition.statespace.EquivalenceClassStore; import tools.refinery.store.statecoding.StateCoderResult; import tools.refinery.store.statecoding.StateCoderStoreAdapter; @@ -68,8 +65,8 @@ public abstract class CompleteEquivalenceClassStore extends AbstractEquivalenceC if (unresolvedSimilarity == null) { return; } - var outcome = this.stateCoderStoreAdapter.checkEquivalence(unresolvedSimilarity.get(0), - unresolvedSimilarity.get(1)); + var outcome = this.stateCoderStoreAdapter.checkEquivalence(unresolvedSimilarity.get(0).version(), + unresolvedSimilarity.get(1).version()); if (outcome != StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC) { delegate(unresolvedSimilarity.get(1), unresolvedSimilarity.activationSizes, unresolvedSimilarity.accept); } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java index b0b416e1..36517709 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/CRAExamplesTest.java @@ -5,29 +5,20 @@ */ package tools.refinery.store.dse; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; -import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.dse.transition.TransformationRule; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.dnf.RelationalQuery; -import tools.refinery.store.dse.transition.TransformationRule; -import tools.refinery.store.dse.strategy.BestFirstStrategy; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.ModelVisualizerAdapter; -import tools.refinery.visualization.internal.FileFormat; import java.util.List; import static tools.refinery.store.query.literal.Literals.not; class CRAExamplesTest { + private static final Symbol name = Symbol.of("Name", 1, String.class); // private static final Symbol classModel = Symbol.of("ClassModel", 1); @@ -130,42 +121,42 @@ class CRAExamplesTest { }); }); - private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", - deleteEmptyClassPrecondition, - (model) -> { -// var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - // TODO: can we move dseAdapter outside? - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// var modelElement = activation.get(0); - var classElement = activation.get(0); - -// classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - dseAdapter.deleteObject(Tuple.of(classElement)); - }); - }); - - private static final TransformationRule createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classElementInterpretation = model.getInterpretation(classElement); -// var classesInterpretation = model.getInterpretation(classes); - var encapsulatesInterpretation = model.getInterpretation(encapsulates); - return ((Tuple activation) -> { - // TODO: can we move dseAdapter outside? - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// var modelElement = activation.get(0); - var feature = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - classElementInterpretation.put(newClassElement, true); -// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true); - }); - }); +// private static final TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", +// deleteEmptyClassPrecondition, +// (model) -> { +//// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// // TODO: can we move dseAdapter outside? +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +//// var modelElement = activation.get(0); +// var classElement = activation.get(0); +// +//// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// dseAdapter.deleteObject(Tuple.of(classElement)); +// }); +// }); + +// private static final TransformationRule createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classElementInterpretation = model.getInterpretation(classElement); +//// var classesInterpretation = model.getInterpretation(classes); +// var encapsulatesInterpretation = model.getInterpretation(encapsulates); +// return ((Tuple activation) -> { +// // TODO: can we move dseAdapter outside? +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +//// var modelElement = activation.get(0); +// var feature = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// classElementInterpretation.put(newClassElement, true); +//// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// encapsulatesInterpretation.put(Tuple.of(newClassElementId, feature), true); +// }); +// }); private static final TransformationRule moveFeatureRule = new TransformationRule("MoveFeature", moveFeaturePrecondition, @@ -181,106 +172,106 @@ class CRAExamplesTest { }); }); - @Test - @Disabled("This test is only for debugging purposes") - void craTest() { - var store = ModelStore.builder() - .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, - functionalDependency, name) - .with(ViatraModelQueryAdapter.builder() - .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, - deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, - moveFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder() - .withOutputpath("test_output") - .withFormat(FileFormat.DOT) - .withFormat(FileFormat.SVG) - .saveStates() - .saveDesignSpace() - ) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) - .objectives(new AlwaysSatisfiedRandomHardObjective()) -// .strategy(new DepthFirstStrategy().withDepthLimit(3).continueIfHardObjectivesFulfilled() - .strategy(new BestFirstStrategy().withDepthLimit(6).continueIfHardObjectivesFulfilled() -// .goOnOnlyIfFitnessIsBetter() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// dseAdapter.setRandom(1); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - -// var modelInterpretation = model.getInterpretation(classModel); - var nameInterpretation = model.getInterpretation(name); - var methodInterpretation = model.getInterpretation(method); - var attributeInterpretation = model.getInterpretation(attribute); - var dataDependencyInterpretation = model.getInterpretation(dataDependency); - var functionalDependencyInterpretation = model.getInterpretation(functionalDependency); - -// var modelElement = dseAdapter.createObject(); - var method1 = dseAdapter.createObject(); - var method1Id = method1.get(0); - var method2 = dseAdapter.createObject(); - var method2Id = method2.get(0); - var method3 = dseAdapter.createObject(); - var method3Id = method3.get(0); - var method4 = dseAdapter.createObject(); - var method4Id = method4.get(0); - var attribute1 = dseAdapter.createObject(); - var attribute1Id = attribute1.get(0); - var attribute2 = dseAdapter.createObject(); - var attribute2Id = attribute2.get(0); - var attribute3 = dseAdapter.createObject(); - var attribute3Id = attribute3.get(0); - var attribute4 = dseAdapter.createObject(); - var attribute4Id = attribute4.get(0); - var attribute5 = dseAdapter.createObject(); - var attribute5Id = attribute5.get(0); - - nameInterpretation.put(method1, "M1"); - nameInterpretation.put(method2, "M2"); - nameInterpretation.put(method3, "M3"); - nameInterpretation.put(method4, "M4"); - nameInterpretation.put(attribute1, "A1"); - nameInterpretation.put(attribute2, "A2"); - nameInterpretation.put(attribute3, "A3"); - nameInterpretation.put(attribute4, "A4"); - nameInterpretation.put(attribute5, "A5"); - - - -// modelInterpretation.put(modelElement, true); - methodInterpretation.put(method1, true); - methodInterpretation.put(method2, true); - methodInterpretation.put(method3, true); - methodInterpretation.put(method4, true); - attributeInterpretation.put(attribute1, true); - attributeInterpretation.put(attribute2, true); - attributeInterpretation.put(attribute3, true); - attributeInterpretation.put(attribute4, true); - attributeInterpretation.put(attribute5, true); - - dataDependencyInterpretation.put(Tuple.of(method1Id, attribute1Id), true); - dataDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); - dataDependencyInterpretation.put(Tuple.of(method2Id, attribute2Id), true); - dataDependencyInterpretation.put(Tuple.of(method3Id, attribute3Id), true); - dataDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); - dataDependencyInterpretation.put(Tuple.of(method4Id, attribute3Id), true); - dataDependencyInterpretation.put(Tuple.of(method4Id, attribute5Id), true); - - functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); - functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute4Id), true); - functionalDependencyInterpretation.put(Tuple.of(method2Id, attribute1Id), true); - functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute1Id), true); - functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); - functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true); - - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - System.out.println("states size: " + states.size()); - } -*/ +// @Test +// @Disabled("This test is only for debugging purposes") +// void craTest() { +// var store = ModelStore.builder() +// .symbols(classElement, encapsulates, classes, features, attribute, method, dataDependency, +// functionalDependency, name) +// .with(ViatraModelQueryAdapter.builder() +// .queries(feature, assignFeaturePreconditionHelper, assignFeaturePrecondition, +// deleteEmptyClassPrecondition, createClassPreconditionHelper, createClassPrecondition, +// moveFeaturePrecondition)) +// .with(ModelVisualizerAdapter.builder() +// .withOutputpath("test_output") +// .withFormat(FileFormat.DOT) +// .withFormat(FileFormat.SVG) +// .saveStates() +// .saveDesignSpace() +// ) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(assignFeatureRule, deleteEmptyClassRule, createClassRule, moveFeatureRule) +// .objectives(new AlwaysSatisfiedRandomHardObjective()) +//// .strategy(new DepthFirstStrategy().withDepthLimit(3).continueIfHardObjectivesFulfilled() +// .strategy(new BestFirstStrategy().withDepthLimit(6).continueIfHardObjectivesFulfilled() +//// .goOnOnlyIfFitnessIsBetter() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +//// dseAdapter.setRandom(1); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +//// var modelInterpretation = model.getInterpretation(classModel); +// var nameInterpretation = model.getInterpretation(name); +// var methodInterpretation = model.getInterpretation(method); +// var attributeInterpretation = model.getInterpretation(attribute); +// var dataDependencyInterpretation = model.getInterpretation(dataDependency); +// var functionalDependencyInterpretation = model.getInterpretation(functionalDependency); +// +//// var modelElement = dseAdapter.createObject(); +// var method1 = dseAdapter.createObject(); +// var method1Id = method1.get(0); +// var method2 = dseAdapter.createObject(); +// var method2Id = method2.get(0); +// var method3 = dseAdapter.createObject(); +// var method3Id = method3.get(0); +// var method4 = dseAdapter.createObject(); +// var method4Id = method4.get(0); +// var attribute1 = dseAdapter.createObject(); +// var attribute1Id = attribute1.get(0); +// var attribute2 = dseAdapter.createObject(); +// var attribute2Id = attribute2.get(0); +// var attribute3 = dseAdapter.createObject(); +// var attribute3Id = attribute3.get(0); +// var attribute4 = dseAdapter.createObject(); +// var attribute4Id = attribute4.get(0); +// var attribute5 = dseAdapter.createObject(); +// var attribute5Id = attribute5.get(0); +// +// nameInterpretation.put(method1, "M1"); +// nameInterpretation.put(method2, "M2"); +// nameInterpretation.put(method3, "M3"); +// nameInterpretation.put(method4, "M4"); +// nameInterpretation.put(attribute1, "A1"); +// nameInterpretation.put(attribute2, "A2"); +// nameInterpretation.put(attribute3, "A3"); +// nameInterpretation.put(attribute4, "A4"); +// nameInterpretation.put(attribute5, "A5"); +// +// +// +//// modelInterpretation.put(modelElement, true); +// methodInterpretation.put(method1, true); +// methodInterpretation.put(method2, true); +// methodInterpretation.put(method3, true); +// methodInterpretation.put(method4, true); +// attributeInterpretation.put(attribute1, true); +// attributeInterpretation.put(attribute2, true); +// attributeInterpretation.put(attribute3, true); +// attributeInterpretation.put(attribute4, true); +// attributeInterpretation.put(attribute5, true); +// +// dataDependencyInterpretation.put(Tuple.of(method1Id, attribute1Id), true); +// dataDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); +// dataDependencyInterpretation.put(Tuple.of(method2Id, attribute2Id), true); +// dataDependencyInterpretation.put(Tuple.of(method3Id, attribute3Id), true); +// dataDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); +// dataDependencyInterpretation.put(Tuple.of(method4Id, attribute3Id), true); +// dataDependencyInterpretation.put(Tuple.of(method4Id, attribute5Id), true); +// +// functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute3Id), true); +// functionalDependencyInterpretation.put(Tuple.of(method1Id, attribute4Id), true); +// functionalDependencyInterpretation.put(Tuple.of(method2Id, attribute1Id), true); +// functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute1Id), true); +// functionalDependencyInterpretation.put(Tuple.of(method3Id, attribute4Id), true); +// functionalDependencyInterpretation.put(Tuple.of(method4Id, attribute2Id), true); +// +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// System.out.println("states size: " + states.size()); +// } +//*/ } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java index 461d9353..1d757a5f 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DebugTest.java @@ -5,22 +5,9 @@ */ package tools.refinery.store.dse; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import tools.refinery.store.dse.objectives.AlwaysSatisfiedRandomHardObjective; -import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.transition.TransformationRule; -import tools.refinery.store.dse.strategy.DepthFirstStrategy; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; -import tools.refinery.visualization.ModelVisualizerAdapter; -import tools.refinery.visualization.internal.FileFormat; class DebugTest { private static final Symbol classModel = Symbol.of("ClassModel", 1); @@ -42,88 +29,88 @@ class DebugTest { private static final AnySymbolView classesView = new KeyOnlyView<>(classes); - @Test - @Disabled("This test is only for debugging purposes") - void BFSTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(ModelVisualizerAdapter.builder() - .withOutputpath("test_output") - .withFormat(FileFormat.DOT) - .withFormat(FileFormat.SVG) - .saveStates() - .saveDesignSpace() - ) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .objectives(new AlwaysSatisfiedRandomHardObjective()) - .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() -// .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() -// .goOnOnlyIfFitnessIsBetter() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); -// dseAdapter.setRandom(1); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var modelElement = dseAdapter.createObject(); - modelElementInterpretation.put(modelElement, true); - classElementInterpretation.put(modelElement, true); - queryEngine.flushChanges(); - - - var states = dseAdapter.explore(); - System.out.println("states size: " + states.size()); - - } +// @Test +// @Disabled("This test is only for debugging purposes") +// void BFSTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createFeatureRule = new TransformationRule("CreateFeature", +// createFeaturePrecondition, +// (model) -> { +// var featuresInterpretation = model.getInterpretation(features); +// var featureInterpretation = model.getInterpretation(feature); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// featureInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition, createFeaturePrecondition)) +// .with(ModelVisualizerAdapter.builder() +// .withOutputpath("test_output") +// .withFormat(FileFormat.DOT) +// .withFormat(FileFormat.SVG) +// .saveStates() +// .saveDesignSpace() +// ) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule, createFeatureRule) +// .objectives(new AlwaysSatisfiedRandomHardObjective()) +// .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() +//// .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() +//// .goOnOnlyIfFitnessIsBetter() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +//// dseAdapter.setRandom(1); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// var classElementInterpretation = model.getInterpretation(classElement); +// var modelElement = dseAdapter.createObject(); +// modelElementInterpretation.put(modelElement, true); +// classElementInterpretation.put(modelElement, true); +// queryEngine.flushChanges(); +// +// +// var states = dseAdapter.explore(); +// System.out.println("states size: " + states.size()); +// +// } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java index 083f7ec3..f5f13433 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/DesignSpaceExplorationTest.java @@ -5,21 +5,9 @@ */ package tools.refinery.store.dse; -import org.junit.jupiter.api.Test; -import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.transition.TransformationRule; -import tools.refinery.store.dse.strategy.BestFirstStrategy; -import tools.refinery.store.dse.strategy.DepthFirstStrategy; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.KeyOnlyView; import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; - -import static org.junit.jupiter.api.Assertions.assertEquals; class DesignSpaceExplorationTest { // private static final Symbol namedElement = Symbol.of("NamedElement", 1); @@ -46,563 +34,563 @@ class DesignSpaceExplorationTest { private static final AnySymbolView featuresView = new KeyOnlyView<>(features); private static final AnySymbolView classesView = new KeyOnlyView<>(classes); - @Test - void createObjectTest() { - var store = ModelStore.builder() - .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - assertEquals(0, dseAdapter.getModelSize()); - - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - assertEquals(0, newModelId); - assertEquals(1, newClass1Id); - assertEquals(2, newClass2Id); - assertEquals(3, newFieldId); - assertEquals(4, dseAdapter.getModelSize()); - } - - @Test - void deleteMiddleObjectTest() { - var store = ModelStore.builder() - .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy())) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - assertEquals(0, dseAdapter.getModelSize()); - - var newObject0 = dseAdapter.createObject(); - var newObject0Id = newObject0.get(0); - var newObject1 = dseAdapter.createObject(); - var newObject1Id = newObject1.get(0); - var newObject2 = dseAdapter.createObject(); - var newObject2Id = newObject2.get(0); - var newObject3 = dseAdapter.createObject(); - var newObject3Id = newObject3.get(0); - - assertEquals(0, newObject0Id); - assertEquals(1, newObject1Id); - assertEquals(2, newObject2Id); - assertEquals(3, newObject3Id); - assertEquals(4, dseAdapter.getModelSize()); - - dseAdapter.deleteObject(newObject1); - assertEquals(4, dseAdapter.getModelSize()); - - var newObject4 = dseAdapter.createObject(); - var newObject4Id = newObject4.get(0); - assertEquals(4, newObject4Id); - assertEquals(5, dseAdapter.getModelSize()); - - dseAdapter.deleteObject(newObject4); - assertEquals(5, dseAdapter.getModelSize()); - } - - @Test - void DFSTrivialTest() { - var store = ModelStore.builder() - .symbols(classModel) - .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - } - - @Test - void DFSOneRuleTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new DepthFirstStrategy().withDepthLimit(0) - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - } - - @Test - void DFSContinueTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(5, states.size()); - } - - @Test - void DFSCompletenessTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new DepthFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(2047, states.size()); - } - - @Test - void DFSSolutionLimitTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new DepthFirstStrategy().withSolutionLimit(222) - .continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(222, states.size()); - } - - @Test - void BeFSTrivialTest() { - var store = ModelStore.builder() - .symbols(classModel) - .with(ViatraModelQueryAdapter.builder()) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new BestFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - } - - @Test - void BeFSOneRuleTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new BestFirstStrategy().withDepthLimit(4) - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(1, states.size()); - } - - @Test - void BeFSContinueTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule) - .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(5, states.size()); - } - - @Test - void BeFSCompletenessTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new BestFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(2047, states.size()); - } - - @Test - void BeFSSolutionLimitTest() { - var createClassPrecondition = Query.of("CreateClassPrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createClassRule = new TransformationRule("CreateClass", - createClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - classElementInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", - (builder, model) -> builder.clause( - classModelView.call(model) - )); - - var createFeatureRule = new TransformationRule("CreateFeature", - createFeaturePrecondition, - (model) -> { - var featuresInterpretation = model.getInterpretation(features); - var featureInterpretation = model.getInterpretation(feature); - return ((Tuple activation) -> { - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var modelElement = activation.get(0); - - var newClassElement = dseAdapter.createObject(); - var newClassElementId = newClassElement.get(0); - - featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); - featureInterpretation.put(Tuple.of(newClassElementId), true); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) - .with(ViatraModelQueryAdapter.builder() - .queries(createClassPrecondition, createFeaturePrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .transformations(createClassRule, createFeatureRule) - .strategy(new BestFirstStrategy().withSolutionLimit(222) - .continueIfHardObjectivesFulfilled() - )) - .build(); - - var model = store.createEmptyModel(); - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - - var modelElementInterpretation = model.getInterpretation(classModel); - modelElementInterpretation.put(dseAdapter.createObject(), true); - queryEngine.flushChanges(); - - var states = dseAdapter.explore(); - assertEquals(222, states.size()); - } +// @Test +// void createObjectTest() { +// var store = ModelStore.builder() +// .with(ViatraModelQueryAdapter.builder()) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// +// assertEquals(0, dseAdapter.getModelSize()); +// +// var newModel = dseAdapter.createObject(); +// var newModelId = newModel.get(0); +// var newClass1 = dseAdapter.createObject(); +// var newClass1Id = newClass1.get(0); +// var newClass2 = dseAdapter.createObject(); +// var newClass2Id = newClass2.get(0); +// var newField = dseAdapter.createObject(); +// var newFieldId = newField.get(0); +// +// assertEquals(0, newModelId); +// assertEquals(1, newClass1Id); +// assertEquals(2, newClass2Id); +// assertEquals(3, newFieldId); +// assertEquals(4, dseAdapter.getModelSize()); +// } + +// @Test +// void deleteMiddleObjectTest() { +// var store = ModelStore.builder() +// .with(ViatraModelQueryAdapter.builder()) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy())) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// +// assertEquals(0, dseAdapter.getModelSize()); +// +// var newObject0 = dseAdapter.createObject(); +// var newObject0Id = newObject0.get(0); +// var newObject1 = dseAdapter.createObject(); +// var newObject1Id = newObject1.get(0); +// var newObject2 = dseAdapter.createObject(); +// var newObject2Id = newObject2.get(0); +// var newObject3 = dseAdapter.createObject(); +// var newObject3Id = newObject3.get(0); +// +// assertEquals(0, newObject0Id); +// assertEquals(1, newObject1Id); +// assertEquals(2, newObject2Id); +// assertEquals(3, newObject3Id); +// assertEquals(4, dseAdapter.getModelSize()); +// +// dseAdapter.deleteObject(newObject1); +// assertEquals(4, dseAdapter.getModelSize()); +// +// var newObject4 = dseAdapter.createObject(); +// var newObject4Id = newObject4.get(0); +// assertEquals(4, newObject4Id); +// assertEquals(5, dseAdapter.getModelSize()); +// +// dseAdapter.deleteObject(newObject4); +// assertEquals(5, dseAdapter.getModelSize()); +// } +// +// @Test +// void DFSTrivialTest() { +// var store = ModelStore.builder() +// .symbols(classModel) +// .with(ViatraModelQueryAdapter.builder()) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// +// var states = dseAdapter.explore(); +// assertEquals(1, states.size()); +// } +// +// @Test +// void DFSOneRuleTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule) +// .strategy(new DepthFirstStrategy().withDepthLimit(0) +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(1, states.size()); +// } +// +// @Test +// void DFSContinueTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule) +// .strategy(new DepthFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(5, states.size()); +// } +// +// @Test +// void DFSCompletenessTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createFeatureRule = new TransformationRule("CreateFeature", +// createFeaturePrecondition, +// (model) -> { +// var featuresInterpretation = model.getInterpretation(features); +// var featureInterpretation = model.getInterpretation(feature); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// featureInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition, createFeaturePrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule, createFeatureRule) +// .strategy(new DepthFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(2047, states.size()); +// } +// +// @Test +// void DFSSolutionLimitTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createFeatureRule = new TransformationRule("CreateFeature", +// createFeaturePrecondition, +// (model) -> { +// var featuresInterpretation = model.getInterpretation(features); +// var featureInterpretation = model.getInterpretation(feature); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// featureInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition, createFeaturePrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule, createFeatureRule) +// .strategy(new DepthFirstStrategy().withSolutionLimit(222) +// .continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(222, states.size()); +// } +// +// @Test +// void BeFSTrivialTest() { +// var store = ModelStore.builder() +// .symbols(classModel) +// .with(ViatraModelQueryAdapter.builder()) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new BestFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// +// var states = dseAdapter.explore(); +// assertEquals(1, states.size()); +// } +// +// @Test +// void BeFSOneRuleTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule) +// .strategy(new BestFirstStrategy().withDepthLimit(4) +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(1, states.size()); +// } +// +// @Test +// void BeFSContinueTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule) +// .strategy(new BestFirstStrategy().withDepthLimit(4).continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(5, states.size()); +// } +// +// @Test +// void BeFSCompletenessTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createFeatureRule = new TransformationRule("CreateFeature", +// createFeaturePrecondition, +// (model) -> { +// var featuresInterpretation = model.getInterpretation(features); +// var featureInterpretation = model.getInterpretation(feature); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// featureInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition, createFeaturePrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule, createFeatureRule) +// .strategy(new BestFirstStrategy().withDepthLimit(10).continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(2047, states.size()); +// } +// +// @Test +// void BeFSSolutionLimitTest() { +// var createClassPrecondition = Query.of("CreateClassPrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createClassRule = new TransformationRule("CreateClass", +// createClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// classesInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// classElementInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var createFeaturePrecondition = Query.of("CreateFeaturePrecondition", +// (builder, model) -> builder.clause( +// classModelView.call(model) +// )); +// +// var createFeatureRule = new TransformationRule("CreateFeature", +// createFeaturePrecondition, +// (model) -> { +// var featuresInterpretation = model.getInterpretation(features); +// var featureInterpretation = model.getInterpretation(feature); +// return ((Tuple activation) -> { +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var modelElement = activation.get(0); +// +// var newClassElement = dseAdapter.createObject(); +// var newClassElementId = newClassElement.get(0); +// +// featuresInterpretation.put(Tuple.of(modelElement, newClassElementId), true); +// featureInterpretation.put(Tuple.of(newClassElementId), true); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, classes, feature, features, isEncapsulatedBy, encapsulates) +// .with(ViatraModelQueryAdapter.builder() +// .queries(createClassPrecondition, createFeaturePrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .transformations(createClassRule, createFeatureRule) +// .strategy(new BestFirstStrategy().withSolutionLimit(222) +// .continueIfHardObjectivesFulfilled() +// )) +// .build(); +// +// var model = store.createEmptyModel(); +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// +// var modelElementInterpretation = model.getInterpretation(classModel); +// modelElementInterpretation.put(dseAdapter.createObject(), true); +// queryEngine.flushChanges(); +// +// var states = dseAdapter.explore(); +// assertEquals(222, states.size()); +// } } diff --git a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java index 1672b63a..43b04e0d 100644 --- a/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java +++ b/subprojects/store-dse/src/test/java/tools/refinery/store/dse/TransformationRuleTest.java @@ -6,409 +6,409 @@ package tools.refinery.store.dse; import org.junit.jupiter.api.Test; -import tools.refinery.store.dse.strategy.DepthFirstStrategy; -import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.ModelQueryAdapter; -import tools.refinery.store.query.dnf.Query; -import tools.refinery.store.dse.transition.TransformationRule; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; -import tools.refinery.store.query.view.AnySymbolView; -import tools.refinery.store.query.view.KeyOnlyView; -import tools.refinery.store.representation.Symbol; -import tools.refinery.store.tuple.Tuple; - -import java.util.List; -import java.util.Map; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static tools.refinery.store.query.literal.Literals.not; -import static tools.refinery.store.dse.tests.QueryAssertions.assertResults; - +// +//import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter; +//import tools.refinery.store.model.ModelStore; +//import tools.refinery.store.query.ModelQueryAdapter; +//import tools.refinery.store.query.dnf.Query; +//import tools.refinery.store.dse.transition.TransformationRule; +//import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +//import tools.refinery.store.query.view.AnySymbolView; +//import tools.refinery.store.query.view.KeyOnlyView; +//import tools.refinery.store.representation.Symbol; +//import tools.refinery.store.tuple.Tuple; +// +//import java.util.List; +//import java.util.Map; +// +//import static org.junit.jupiter.api.Assertions.assertEquals; +//import static tools.refinery.store.query.literal.Literals.not; +//import static tools.refinery.store.dse.tests.QueryAssertions.assertResults; +// class TransformationRuleTest { - - private static final Symbol classModel = Symbol.of("ClassModel", 1); - private static final Symbol classElement = Symbol.of("ClassElement", 1); - private static final Symbol feature = Symbol.of("Feature", 1); - - private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); - private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); - - private static final Symbol features = Symbol.of("Features", 2); - private static final Symbol classes = Symbol.of("Classes", 2); - - private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); - private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); - private static final AnySymbolView featureView = new KeyOnlyView<>(feature); - private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); - private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); - private static final AnySymbolView featuresView = new KeyOnlyView<>(features); - private static final AnySymbolView classesView = new KeyOnlyView<>(classes); - - @Test - void activationsTest() { - var assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", - (builder, model, c, f) -> builder.clause( - classElementView.call(c), - classesView.call(model, c), - encapsulatesView.call(c, f) - )); - - var assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f) - -> builder.clause((model, c1) -> List.of( - classModelView.call(model), - featureView.call(f), - classElementView.call(c2), - featuresView.call(model, f), - classesView.call(model, c1), - not(assignFeaturePreconditionHelper.call(model, c2, f)), - not(encapsulatesView.call(c2, f)) - ))); - - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", - assignFeaturePrecondition, - (model) -> { - var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); - return ((Tuple activation) -> { - var feature = activation.get(0); - var classElement = activation.get(1); - - isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); - }); - }); - - TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }); - - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(assignFeaturePrecondition, assignFeaturePreconditionHelper, - deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - assignFeatureRule.prepare(model, queryEngine); - deleteEmptyClassRule.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsResultSet(); - var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsResultSet(); - - assertResults(Map.of( - Tuple.of(newClass1Id, newFieldId), true, - Tuple.of(newClass2Id, newFieldId), true - ), assignFeatureRuleActivations); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRuleActivations); - } - - @Test - void randomActivationTest() { - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule deleteEmptyClassRule0 = new TransformationRule("DeleteEmptyClass0", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }, - 0L); - - TransformationRule deleteEmptyClassRule1 = new TransformationRule("DeleteEmptyClass1", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }, - 78634L); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - deleteEmptyClassRule0.prepare(model, queryEngine); - deleteEmptyClassRule1.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - - var activation0 = deleteEmptyClassRule0.getRandomActivation().activation(); - var activation1 = deleteEmptyClassRule1.getRandomActivation().activation(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule0.getAllActivationsAsResultSet()); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule1.getAllActivationsAsResultSet()); - - assertEquals(Tuple.of(newModelId, newClass2Id), activation0); - assertEquals(Tuple.of(newModelId, newClass1Id), activation1); - - } - - @Test - void fireTest() { - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - deleteEmptyClassRule.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsResultSet()); - - - deleteEmptyClassRule.fireActivation(Tuple.of(0, 1)); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), false, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsResultSet()); - } - - @Test - void randomFireTest() { - var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", - (builder, model, c) -> builder.clause((f) -> List.of( - classModelView.call(model), - classElementView.call(c), - featuresView.call(model, f), - not(encapsulatesView.call(c, f)) - ))); - - TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass0", - deleteEmptyClassPrecondition, - (model) -> { - var classesInterpretation = model.getInterpretation(classes); - var classElementInterpretation = model.getInterpretation(classElement); - return ((Tuple activation) -> { - var modelElement = activation.get(0); - var classElement = activation.get(1); - - classesInterpretation.put(Tuple.of(modelElement, classElement), false); - classElementInterpretation.put(Tuple.of(classElement), false); - }); - }, - 0L); - - var store = ModelStore.builder() - .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) - .with(ViatraModelQueryAdapter.builder() - .queries(deleteEmptyClassPrecondition)) - .with(DesignSpaceExplorationAdapter.builder() - .strategy(new DepthFirstStrategy().withDepthLimit(0))) - .build(); - - var model = store.createEmptyModel(); - var queryEngine = model.getAdapter(ModelQueryAdapter.class); - deleteEmptyClassRule.prepare(model, queryEngine); - - var classModelInterpretation = model.getInterpretation(classModel); - var classElementInterpretation = model.getInterpretation(classElement); - var featureInterpretation = model.getInterpretation(feature); - var featuresInterpretation = model.getInterpretation(features); - var classesInterpretation = model.getInterpretation(classes); - - var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); - var newModel = dseAdapter.createObject(); - var newModelId = newModel.get(0); - var newClass1 = dseAdapter.createObject(); - var newClass1Id = newClass1.get(0); - var newClass2 = dseAdapter.createObject(); - var newClass2Id = newClass2.get(0); - var newField = dseAdapter.createObject(); - var newFieldId = newField.get(0); - - classModelInterpretation.put(newModel, true); - classElementInterpretation.put(newClass1, true); - classElementInterpretation.put(newClass2, true); - featureInterpretation.put(newField, true); - classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); - classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); - featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); - - queryEngine.flushChanges(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), true - ), deleteEmptyClassRule.getAllActivationsAsResultSet()); - - deleteEmptyClassRule.fireRandomActivation(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), true, - Tuple.of(newModelId, newClass2Id), false - ), deleteEmptyClassRule.getAllActivationsAsResultSet()); - - deleteEmptyClassRule.fireRandomActivation(); - - assertResults(Map.of( - Tuple.of(newModelId, newClass1Id), false, - Tuple.of(newModelId, newClass2Id), false - ), deleteEmptyClassRule.getAllActivationsAsResultSet()); - - } +// +// private static final Symbol classModel = Symbol.of("ClassModel", 1); +// private static final Symbol classElement = Symbol.of("ClassElement", 1); +// private static final Symbol feature = Symbol.of("Feature", 1); +// +// private static final Symbol isEncapsulatedBy = Symbol.of("IsEncapsulatedBy", 2); +// private static final Symbol encapsulates = Symbol.of("Encapsulates", 2); +// +// private static final Symbol features = Symbol.of("Features", 2); +// private static final Symbol classes = Symbol.of("Classes", 2); +// +// private static final AnySymbolView classModelView = new KeyOnlyView<>(classModel); +// private static final AnySymbolView classElementView = new KeyOnlyView<>(classElement); +// private static final AnySymbolView featureView = new KeyOnlyView<>(feature); +// private static final AnySymbolView isEncapsulatedByView = new KeyOnlyView<>(isEncapsulatedBy); +// private static final AnySymbolView encapsulatesView = new KeyOnlyView<>(encapsulates); +// private static final AnySymbolView featuresView = new KeyOnlyView<>(features); +// private static final AnySymbolView classesView = new KeyOnlyView<>(classes); +// +// @Test +// void activationsTest() { +// var assignFeaturePreconditionHelper = Query.of("AssignFeaturePreconditionHelper", +// (builder, model, c, f) -> builder.clause( +// classElementView.call(c), +// classesView.call(model, c), +// encapsulatesView.call(c, f) +// )); +// +// var assignFeaturePrecondition = Query.of("AssignFeaturePrecondition", (builder, c2, f) +// -> builder.clause((model, c1) -> List.of( +// classModelView.call(model), +// featureView.call(f), +// classElementView.call(c2), +// featuresView.call(model, f), +// classesView.call(model, c1), +// not(assignFeaturePreconditionHelper.call(model, c2, f)), +// not(encapsulatesView.call(c2, f)) +// ))); +// +// var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", +// (builder, model, c) -> builder.clause((f) -> List.of( +// classModelView.call(model), +// classElementView.call(c), +// featuresView.call(model, f), +// not(encapsulatesView.call(c, f)) +// ))); +// +// TransformationRule assignFeatureRule = new TransformationRule("AssignFeature", +// assignFeaturePrecondition, +// (model) -> { +// var isEncapsulatedByInterpretation = model.getInterpretation(isEncapsulatedBy); +// return ((Tuple activation) -> { +// var feature = activation.get(0); +// var classElement = activation.get(1); +// +// isEncapsulatedByInterpretation.put(Tuple.of(feature, classElement), true); +// }); +// }); +// +// TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", +// deleteEmptyClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var modelElement = activation.get(0); +// var classElement = activation.get(1); +// +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// }); +// }); +// +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) +// .with(ViatraModelQueryAdapter.builder() +// .queries(assignFeaturePrecondition, assignFeaturePreconditionHelper, +// deleteEmptyClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// assignFeatureRule.prepare(model, queryEngine); +// deleteEmptyClassRule.prepare(model, queryEngine); +// +// var classModelInterpretation = model.getInterpretation(classModel); +// var classElementInterpretation = model.getInterpretation(classElement); +// var featureInterpretation = model.getInterpretation(feature); +// var featuresInterpretation = model.getInterpretation(features); +// var classesInterpretation = model.getInterpretation(classes); +// +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var newModel = dseAdapter.createObject(); +// var newModelId = newModel.get(0); +// var newClass1 = dseAdapter.createObject(); +// var newClass1Id = newClass1.get(0); +// var newClass2 = dseAdapter.createObject(); +// var newClass2Id = newClass2.get(0); +// var newField = dseAdapter.createObject(); +// var newFieldId = newField.get(0); +// +// classModelInterpretation.put(newModel, true); +// classElementInterpretation.put(newClass1, true); +// classElementInterpretation.put(newClass2, true); +// featureInterpretation.put(newField, true); +// classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); +// classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); +// featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); +// +// queryEngine.flushChanges(); +// +// var assignFeatureRuleActivations = assignFeatureRule.getAllActivationsAsResultSet(); +// var deleteEmptyClassRuleActivations = deleteEmptyClassRule.getAllActivationsAsResultSet(); +// +// assertResults(Map.of( +// Tuple.of(newClass1Id, newFieldId), true, +// Tuple.of(newClass2Id, newFieldId), true +// ), assignFeatureRuleActivations); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRuleActivations); +// } +// +// @Test +// void randomActivationTest() { +// var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", +// (builder, model, c) -> builder.clause((f) -> List.of( +// classModelView.call(model), +// classElementView.call(c), +// featuresView.call(model, f), +// not(encapsulatesView.call(c, f)) +// ))); +// +// TransformationRule deleteEmptyClassRule0 = new TransformationRule("DeleteEmptyClass0", +// deleteEmptyClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var modelElement = activation.get(0); +// var classElement = activation.get(1); +// +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// }); +// }, +// 0L); +// +// TransformationRule deleteEmptyClassRule1 = new TransformationRule("DeleteEmptyClass1", +// deleteEmptyClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var modelElement = activation.get(0); +// var classElement = activation.get(1); +// +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// }); +// }, +// 78634L); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) +// .with(ViatraModelQueryAdapter.builder() +// .queries(deleteEmptyClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// deleteEmptyClassRule0.prepare(model, queryEngine); +// deleteEmptyClassRule1.prepare(model, queryEngine); +// +// var classModelInterpretation = model.getInterpretation(classModel); +// var classElementInterpretation = model.getInterpretation(classElement); +// var featureInterpretation = model.getInterpretation(feature); +// var featuresInterpretation = model.getInterpretation(features); +// var classesInterpretation = model.getInterpretation(classes); +// +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var newModel = dseAdapter.createObject(); +// var newModelId = newModel.get(0); +// var newClass1 = dseAdapter.createObject(); +// var newClass1Id = newClass1.get(0); +// var newClass2 = dseAdapter.createObject(); +// var newClass2Id = newClass2.get(0); +// var newField = dseAdapter.createObject(); +// var newFieldId = newField.get(0); +// +// classModelInterpretation.put(newModel, true); +// classElementInterpretation.put(newClass1, true); +// classElementInterpretation.put(newClass2, true); +// featureInterpretation.put(newField, true); +// classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); +// classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); +// featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); +// +// queryEngine.flushChanges(); +// +// +// var activation0 = deleteEmptyClassRule0.getRandomActivation().activation(); +// var activation1 = deleteEmptyClassRule1.getRandomActivation().activation(); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRule0.getAllActivationsAsResultSet()); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRule1.getAllActivationsAsResultSet()); +// +// assertEquals(Tuple.of(newModelId, newClass2Id), activation0); +// assertEquals(Tuple.of(newModelId, newClass1Id), activation1); +// +// } +// +// @Test +// void fireTest() { +// var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", +// (builder, model, c) -> builder.clause((f) -> List.of( +// classModelView.call(model), +// classElementView.call(c), +// featuresView.call(model, f), +// not(encapsulatesView.call(c, f)) +// ))); +// +// TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass", +// deleteEmptyClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var modelElement = activation.get(0); +// var classElement = activation.get(1); +// +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// }); +// }); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) +// .with(ViatraModelQueryAdapter.builder() +// .queries(deleteEmptyClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// deleteEmptyClassRule.prepare(model, queryEngine); +// +// var classModelInterpretation = model.getInterpretation(classModel); +// var classElementInterpretation = model.getInterpretation(classElement); +// var featureInterpretation = model.getInterpretation(feature); +// var featuresInterpretation = model.getInterpretation(features); +// var classesInterpretation = model.getInterpretation(classes); +// +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var newModel = dseAdapter.createObject(); +// var newModelId = newModel.get(0); +// var newClass1 = dseAdapter.createObject(); +// var newClass1Id = newClass1.get(0); +// var newClass2 = dseAdapter.createObject(); +// var newClass2Id = newClass2.get(0); +// var newField = dseAdapter.createObject(); +// var newFieldId = newField.get(0); +// +// classModelInterpretation.put(newModel, true); +// classElementInterpretation.put(newClass1, true); +// classElementInterpretation.put(newClass2, true); +// featureInterpretation.put(newField, true); +// classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); +// classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); +// featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); +// +// queryEngine.flushChanges(); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRule.getAllActivationsAsResultSet()); +// +// +// deleteEmptyClassRule.fireActivation(Tuple.of(0, 1)); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), false, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRule.getAllActivationsAsResultSet()); +// } +// +// @Test +// void randomFireTest() { +// var deleteEmptyClassPrecondition = Query.of("DeleteEmptyClassPrecondition", +// (builder, model, c) -> builder.clause((f) -> List.of( +// classModelView.call(model), +// classElementView.call(c), +// featuresView.call(model, f), +// not(encapsulatesView.call(c, f)) +// ))); +// +// TransformationRule deleteEmptyClassRule = new TransformationRule("DeleteEmptyClass0", +// deleteEmptyClassPrecondition, +// (model) -> { +// var classesInterpretation = model.getInterpretation(classes); +// var classElementInterpretation = model.getInterpretation(classElement); +// return ((Tuple activation) -> { +// var modelElement = activation.get(0); +// var classElement = activation.get(1); +// +// classesInterpretation.put(Tuple.of(modelElement, classElement), false); +// classElementInterpretation.put(Tuple.of(classElement), false); +// }); +// }, +// 0L); +// +// var store = ModelStore.builder() +// .symbols(classModel, classElement, feature, isEncapsulatedBy, encapsulates, classes, features) +// .with(ViatraModelQueryAdapter.builder() +// .queries(deleteEmptyClassPrecondition)) +// .with(DesignSpaceExplorationAdapter.builder() +// .strategy(new DepthFirstStrategy().withDepthLimit(0))) +// .build(); +// +// var model = store.createEmptyModel(); +// var queryEngine = model.getAdapter(ModelQueryAdapter.class); +// deleteEmptyClassRule.prepare(model, queryEngine); +// +// var classModelInterpretation = model.getInterpretation(classModel); +// var classElementInterpretation = model.getInterpretation(classElement); +// var featureInterpretation = model.getInterpretation(feature); +// var featuresInterpretation = model.getInterpretation(features); +// var classesInterpretation = model.getInterpretation(classes); +// +// var dseAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class); +// var newModel = dseAdapter.createObject(); +// var newModelId = newModel.get(0); +// var newClass1 = dseAdapter.createObject(); +// var newClass1Id = newClass1.get(0); +// var newClass2 = dseAdapter.createObject(); +// var newClass2Id = newClass2.get(0); +// var newField = dseAdapter.createObject(); +// var newFieldId = newField.get(0); +// +// classModelInterpretation.put(newModel, true); +// classElementInterpretation.put(newClass1, true); +// classElementInterpretation.put(newClass2, true); +// featureInterpretation.put(newField, true); +// classesInterpretation.put(Tuple.of(newModelId, newClass1Id), true); +// classesInterpretation.put(Tuple.of(newModelId, newClass2Id), true); +// featuresInterpretation.put(Tuple.of(newModelId, newFieldId), true); +// +// queryEngine.flushChanges(); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), true +// ), deleteEmptyClassRule.getAllActivationsAsResultSet()); +// +// deleteEmptyClassRule.fireRandomActivation(); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), true, +// Tuple.of(newModelId, newClass2Id), false +// ), deleteEmptyClassRule.getAllActivationsAsResultSet()); +// +// deleteEmptyClassRule.fireRandomActivation(); +// +// assertResults(Map.of( +// Tuple.of(newModelId, newClass1Id), false, +// Tuple.of(newModelId, newClass2Id), false +// ), deleteEmptyClassRule.getAllActivationsAsResultSet()); +// +// } } -- cgit v1.2.3-54-g00ecf