diff options
17 files changed, 1004 insertions, 428 deletions
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 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.statecoding; | ||
7 | |||
8 | import org.eclipse.collections.api.set.primitive.IntSet; | ||
9 | import tools.refinery.store.model.Interpretation; | ||
10 | |||
11 | import java.util.List; | ||
12 | |||
13 | public interface StateCodeCalculatorFactory { | ||
14 | StateCodeCalculator create(List<? extends Interpretation<?>> interpretations, IntSet individuals); | ||
15 | } | ||
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; | |||
8 | import tools.refinery.store.adapter.ModelAdapterBuilder; | 8 | import tools.refinery.store.adapter.ModelAdapterBuilder; |
9 | import tools.refinery.store.model.ModelStore; | 9 | import tools.refinery.store.model.ModelStore; |
10 | import tools.refinery.store.representation.AnySymbol; | 10 | import tools.refinery.store.representation.AnySymbol; |
11 | import tools.refinery.store.tuple.Tuple1; | ||
11 | 12 | ||
13 | import java.util.Arrays; | ||
12 | import java.util.Collection; | 14 | import java.util.Collection; |
13 | import java.util.List; | 15 | import java.util.List; |
14 | 16 | ||
@@ -24,6 +26,20 @@ public interface StateCoderBuilder extends ModelAdapterBuilder { | |||
24 | return excludeAll(List.of(symbols)); | 26 | return excludeAll(List.of(symbols)); |
25 | } | 27 | } |
26 | 28 | ||
29 | StateCoderBuilder individual(Tuple1 tuple); | ||
30 | default StateCoderBuilder individual(Collection<Tuple1> tuple1s) { | ||
31 | for(Tuple1 tuple : tuple1s){ | ||
32 | individual(tuple); | ||
33 | } | ||
34 | return this; | ||
35 | } | ||
36 | default StateCoderBuilder individuals(Tuple1... tuple1s) { | ||
37 | return individual(Arrays.stream(tuple1s).toList()); | ||
38 | } | ||
39 | |||
40 | StateCoderBuilder stateCodeCalculatorFactory(StateCodeCalculatorFactory codeCalculatorFactory); | ||
41 | StateCoderBuilder stateEquivalenceChecker(StateEquivalenceChecker stateEquivalenceChecker); | ||
42 | |||
27 | @Override | 43 | @Override |
28 | StateCoderStoreAdapter build(ModelStore store); | 44 | StateCoderStoreAdapter build(ModelStore store); |
29 | } | 45 | } |
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 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.statecoding; | 6 | package tools.refinery.store.statecoding; |
7 | 7 | ||
8 | import org.eclipse.collections.api.set.primitive.IntSet; | ||
8 | import tools.refinery.store.model.Interpretation; | 9 | import tools.refinery.store.model.Interpretation; |
9 | 10 | ||
10 | import java.util.List; | 11 | import java.util.List; |
@@ -15,6 +16,7 @@ public interface StateEquivalenceChecker { | |||
15 | } | 16 | } |
16 | 17 | ||
17 | EquivalenceResult constructMorphism( | 18 | EquivalenceResult constructMorphism( |
19 | IntSet individuals, | ||
18 | List<? extends Interpretation<?>> interpretations1, | 20 | List<? extends Interpretation<?>> interpretations1, |
19 | ObjectCode code1, List<? | 21 | ObjectCode code1, List<? |
20 | extends Interpretation<?>> interpretations2, | 22 | extends Interpretation<?>> 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 @@ | |||
6 | package tools.refinery.store.statecoding.internal; | 6 | package tools.refinery.store.statecoding.internal; |
7 | 7 | ||
8 | import tools.refinery.store.adapter.ModelStoreAdapter; | 8 | import tools.refinery.store.adapter.ModelStoreAdapter; |
9 | import tools.refinery.store.model.Interpretation; | ||
10 | import tools.refinery.store.model.Model; | 9 | import tools.refinery.store.model.Model; |
11 | import tools.refinery.store.representation.Symbol; | ||
12 | import tools.refinery.store.statecoding.StateCodeCalculator; | 10 | import tools.refinery.store.statecoding.StateCodeCalculator; |
13 | import tools.refinery.store.statecoding.StateCoderAdapter; | 11 | import tools.refinery.store.statecoding.StateCoderAdapter; |
14 | import tools.refinery.store.statecoding.StateCoderResult; | 12 | import tools.refinery.store.statecoding.StateCoderResult; |
15 | import tools.refinery.store.statecoding.neighbourhood.LazyNeighbourhoodCalculator; | ||
16 | |||
17 | import java.util.Collection; | ||
18 | import java.util.List; | ||
19 | 13 | ||
20 | public class StateCoderAdapterImpl implements StateCoderAdapter { | 14 | public class StateCoderAdapterImpl implements StateCoderAdapter { |
21 | final ModelStoreAdapter storeAdapter; | 15 | final ModelStoreAdapter storeAdapter; |
22 | final Model model; | 16 | final Model model; |
23 | final StateCodeCalculator calculator; | 17 | final StateCodeCalculator calculator; |
24 | 18 | ||
25 | StateCoderAdapterImpl(ModelStoreAdapter storeAdapter, Model model, Collection<Symbol<?>> symbols) { | 19 | StateCoderAdapterImpl(ModelStoreAdapter storeAdapter, StateCodeCalculator calculator, Model model) { |
26 | this.storeAdapter = storeAdapter; | 20 | this.storeAdapter = storeAdapter; |
27 | this.model = model; | 21 | this.model = model; |
28 | 22 | this.calculator = calculator; | |
29 | List<? extends Interpretation<?>> interpretations = symbols.stream().map(model::getInterpretation).toList(); | ||
30 | calculator = new LazyNeighbourhoodCalculator(interpretations); | ||
31 | } | 23 | } |
32 | 24 | ||
33 | @Override | 25 | @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..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 | |||
@@ -5,19 +5,24 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.statecoding.internal; | 6 | package tools.refinery.store.statecoding.internal; |
7 | 7 | ||
8 | import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; | ||
8 | import tools.refinery.store.model.ModelStore; | 9 | import tools.refinery.store.model.ModelStore; |
9 | import tools.refinery.store.model.ModelStoreBuilder; | 10 | import tools.refinery.store.model.ModelStoreBuilder; |
10 | import tools.refinery.store.representation.AnySymbol; | 11 | import tools.refinery.store.representation.AnySymbol; |
11 | import tools.refinery.store.representation.Symbol; | 12 | import tools.refinery.store.representation.Symbol; |
12 | import tools.refinery.store.statecoding.StateCoderBuilder; | 13 | import tools.refinery.store.statecoding.*; |
13 | import tools.refinery.store.statecoding.StateCoderStoreAdapter; | 14 | import tools.refinery.store.statecoding.neighbourhood.NeighbourhoodCalculator; |
15 | import tools.refinery.store.statecoding.stateequivalence.StateEquivalenceCheckerImpl; | ||
16 | import tools.refinery.store.tuple.Tuple1; | ||
14 | 17 | ||
15 | import java.util.HashSet; | 18 | import java.util.*; |
16 | import java.util.LinkedHashSet; | ||
17 | import java.util.Set; | ||
18 | 19 | ||
19 | public class StateCoderBuilderImpl implements StateCoderBuilder { | 20 | public class StateCoderBuilderImpl implements StateCoderBuilder { |
20 | Set<AnySymbol> excluded = new HashSet<>(); | 21 | Set<AnySymbol> excluded = new HashSet<>(); |
22 | IntHashSet individuals = new IntHashSet(); | ||
23 | |||
24 | StateCodeCalculatorFactory calculator = NeighbourhoodCalculator::new; | ||
25 | StateEquivalenceChecker checker = new StateEquivalenceCheckerImpl(); | ||
21 | 26 | ||
22 | @Override | 27 | @Override |
23 | public StateCoderBuilder exclude(AnySymbol symbol) { | 28 | public StateCoderBuilder exclude(AnySymbol symbol) { |
@@ -26,6 +31,24 @@ public class StateCoderBuilderImpl implements StateCoderBuilder { | |||
26 | } | 31 | } |
27 | 32 | ||
28 | @Override | 33 | @Override |
34 | public StateCoderBuilder individual(Tuple1 tuple) { | ||
35 | individuals.add(tuple.get(0)); | ||
36 | return this; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public StateCoderBuilder stateEquivalenceChecker(StateEquivalenceChecker stateEquivalenceChecker) { | ||
41 | this.checker = stateEquivalenceChecker; | ||
42 | return this; | ||
43 | } | ||
44 | |||
45 | @Override | ||
46 | public StateCoderBuilder stateCodeCalculatorFactory(StateCodeCalculatorFactory codeCalculatorFactory) { | ||
47 | this.calculator = codeCalculatorFactory; | ||
48 | return this; | ||
49 | } | ||
50 | |||
51 | @Override | ||
29 | public boolean isConfigured() { | 52 | public boolean isConfigured() { |
30 | return true; | 53 | return true; |
31 | } | 54 | } |
@@ -43,6 +66,6 @@ public class StateCoderBuilderImpl implements StateCoderBuilder { | |||
43 | symbols.add(typed); | 66 | symbols.add(typed); |
44 | } | 67 | } |
45 | } | 68 | } |
46 | return new StateCoderStoreAdapterImpl(store, symbols); | 69 | return new StateCoderStoreAdapterImpl(store, calculator, checker, symbols, individuals); |
47 | } | 70 | } |
48 | } | 71 | } |
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 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.statecoding.internal; | 6 | package tools.refinery.store.statecoding.internal; |
7 | 7 | ||
8 | import org.eclipse.collections.api.set.primitive.IntSet; | ||
8 | import tools.refinery.store.map.Version; | 9 | import tools.refinery.store.map.Version; |
9 | import tools.refinery.store.model.Model; | 10 | import tools.refinery.store.model.Model; |
10 | import tools.refinery.store.model.ModelStore; | 11 | import tools.refinery.store.model.ModelStore; |
11 | import tools.refinery.store.representation.Symbol; | 12 | import tools.refinery.store.representation.Symbol; |
13 | import tools.refinery.store.statecoding.StateCodeCalculatorFactory; | ||
12 | import tools.refinery.store.statecoding.StateCoderAdapter; | 14 | import tools.refinery.store.statecoding.StateCoderAdapter; |
13 | import tools.refinery.store.statecoding.StateCoderStoreAdapter; | 15 | import tools.refinery.store.statecoding.StateCoderStoreAdapter; |
14 | import tools.refinery.store.statecoding.StateEquivalenceChecker; | 16 | import tools.refinery.store.statecoding.StateEquivalenceChecker; |
15 | import tools.refinery.store.statecoding.stateequivalence.StateEquivalenceCheckerImpl; | ||
16 | 17 | ||
17 | import java.util.Collection; | 18 | import java.util.Collection; |
18 | import java.util.Objects; | 19 | import java.util.Objects; |
@@ -20,12 +21,22 @@ import java.util.Objects; | |||
20 | public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { | 21 | public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { |
21 | final ModelStore store; | 22 | final ModelStore store; |
22 | final Collection<Symbol<?>> symbols; | 23 | final Collection<Symbol<?>> symbols; |
24 | final IntSet individuals; | ||
23 | 25 | ||
24 | final StateEquivalenceChecker equivalenceChecker = new StateEquivalenceCheckerImpl(); | 26 | final StateEquivalenceChecker equivalenceChecker; |
27 | final StateCodeCalculatorFactory codeCalculatorFactory; | ||
25 | 28 | ||
26 | StateCoderStoreAdapterImpl(ModelStore store, Collection<Symbol<?>> symbols) { | 29 | StateCoderStoreAdapterImpl(ModelStore store, |
30 | StateCodeCalculatorFactory codeCalculatorFactory, | ||
31 | StateEquivalenceChecker equivalenceChecker, | ||
32 | Collection<Symbol<?>> symbols, | ||
33 | IntSet individuals) | ||
34 | { | ||
35 | this.codeCalculatorFactory = codeCalculatorFactory; | ||
36 | this.equivalenceChecker = equivalenceChecker; | ||
27 | this.store = store; | 37 | this.store = store; |
28 | this.symbols = symbols; | 38 | this.symbols = symbols; |
39 | this.individuals = individuals; | ||
29 | } | 40 | } |
30 | 41 | ||
31 | @Override | 42 | @Override |
@@ -35,7 +46,7 @@ public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { | |||
35 | 46 | ||
36 | @Override | 47 | @Override |
37 | public StateEquivalenceChecker.EquivalenceResult checkEquivalence(Version v1, Version v2) { | 48 | public StateEquivalenceChecker.EquivalenceResult checkEquivalence(Version v1, Version v2) { |
38 | if(Objects.equals(v1,v2)) { | 49 | if (Objects.equals(v1, v2)) { |
39 | return StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC; | 50 | return StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC; |
40 | } | 51 | } |
41 | var model1 = this.getStore().createModelForState(v1); | 52 | var model1 = this.getStore().createModelForState(v1); |
@@ -44,20 +55,20 @@ public class StateCoderStoreAdapterImpl implements StateCoderStoreAdapter { | |||
44 | var s1 = model1.getAdapter(StateCoderAdapter.class).calculateStateCode(); | 55 | var s1 = model1.getAdapter(StateCoderAdapter.class).calculateStateCode(); |
45 | var s2 = model2.getAdapter(StateCoderAdapter.class).calculateStateCode(); | 56 | var s2 = model2.getAdapter(StateCoderAdapter.class).calculateStateCode(); |
46 | 57 | ||
47 | if(s1.modelCode() != s2.modelCode()) { | 58 | if (s1.modelCode() != s2.modelCode()) { |
48 | return StateEquivalenceChecker.EquivalenceResult.DIFFERENT; | 59 | return StateEquivalenceChecker.EquivalenceResult.DIFFERENT; |
49 | } | 60 | } |
50 | 61 | ||
51 | var i1 = symbols.stream().map(model1::getInterpretation).toList(); | 62 | var i1 = symbols.stream().map(model1::getInterpretation).toList(); |
52 | var i2 = symbols.stream().map(model2::getInterpretation).toList(); | 63 | var i2 = symbols.stream().map(model2::getInterpretation).toList(); |
53 | 64 | ||
54 | return equivalenceChecker.constructMorphism(i1,s1.objectCode(),i2,s2.objectCode()); | 65 | return equivalenceChecker.constructMorphism(individuals, i1, s1.objectCode(), i2, s2.objectCode()); |
55 | } | 66 | } |
56 | 67 | ||
57 | @Override | 68 | @Override |
58 | public StateCoderAdapter createModelAdapter(Model model) { | 69 | public StateCoderAdapter createModelAdapter(Model model) { |
59 | return new StateCoderAdapterImpl(this,model,symbols); | 70 | var interpretations = symbols.stream().map(model::getInterpretation).toList(); |
71 | var coder = codeCalculatorFactory.create(interpretations, individuals); | ||
72 | return new StateCoderAdapterImpl(this, coder, model); | ||
60 | } | 73 | } |
61 | |||
62 | |||
63 | } | 74 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java new file mode 100644 index 00000000..0a40a19d --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/statecoding/neighbourhood/AbstractNeighbourhoodCalculator.java | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.statecoding.neighbourhood; | ||
7 | |||
8 | import org.eclipse.collections.api.set.primitive.IntSet; | ||
9 | import org.eclipse.collections.impl.map.mutable.primitive.IntLongHashMap; | ||
10 | import tools.refinery.store.model.Interpretation; | ||
11 | import tools.refinery.store.statecoding.ObjectCode; | ||
12 | import tools.refinery.store.tuple.Tuple; | ||
13 | import tools.refinery.store.tuple.Tuple0; | ||
14 | |||
15 | import java.util.*; | ||
16 | |||
17 | public abstract class AbstractNeighbourhoodCalculator { | ||
18 | protected final List<Interpretation<?>> nullImpactValues; | ||
19 | protected final LinkedHashMap<Interpretation<?>, long[]> impactValues; | ||
20 | protected final IntLongHashMap individualHashValues; | ||
21 | |||
22 | protected static final long PRIME = 31; | ||
23 | |||
24 | protected AbstractNeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations, IntSet individuals) { | ||
25 | this.nullImpactValues = new ArrayList<>(); | ||
26 | this.impactValues = new LinkedHashMap<>(); | ||
27 | @SuppressWarnings("squid:S2245") | ||
28 | Random random = new Random(1); | ||
29 | |||
30 | individualHashValues = new IntLongHashMap(); | ||
31 | var individualsInOrder = individuals.toSortedList(Integer::compare); | ||
32 | for(int i = 0; i<individualsInOrder.size(); i++) { | ||
33 | individualHashValues.put(individualsInOrder.get(i), random.nextLong()); | ||
34 | } | ||
35 | |||
36 | for (Interpretation<?> interpretation : interpretations) { | ||
37 | int arity = interpretation.getSymbol().arity(); | ||
38 | if (arity == 0) { | ||
39 | nullImpactValues.add(interpretation); | ||
40 | } else { | ||
41 | long[] impact = new long[arity]; | ||
42 | for (int i = 0; i < arity; i++) { | ||
43 | impact[i] = random.nextInt(); | ||
44 | } | ||
45 | impactValues.put(interpretation, impact); | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | |||
50 | protected void initializeWithIndividuals(ObjectCodeImpl previous) { | ||
51 | for (var entry : individualHashValues.keyValuesView()) { | ||
52 | previous.set(entry.getOne(), entry.getTwo()); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | protected long getTupleHash1(Tuple tuple, Object value, ObjectCode objectCodeImpl) { | ||
57 | long result = Objects.hashCode(value); | ||
58 | result = result * PRIME + objectCodeImpl.get(tuple.get(0)); | ||
59 | return result; | ||
60 | } | ||
61 | |||
62 | protected long getTupleHash2(Tuple tuple, Object value, ObjectCode objectCodeImpl) { | ||
63 | long result = Objects.hashCode(value); | ||
64 | result = result * PRIME + objectCodeImpl.get(tuple.get(0)); | ||
65 | result = result * PRIME + objectCodeImpl.get(tuple.get(1)); | ||
66 | if (tuple.get(0) == tuple.get(1)) { | ||
67 | result += PRIME; | ||
68 | result *= PRIME; | ||
69 | } | ||
70 | return result; | ||
71 | } | ||
72 | |||
73 | protected long getTupleHashN(Tuple tuple, Object value, ObjectCode objectCodeImpl) { | ||
74 | long result = Objects.hashCode(value); | ||
75 | for (int i = 0; i < tuple.getSize(); i++) { | ||
76 | result = result * PRIME + objectCodeImpl.get(tuple.get(i)); | ||
77 | } | ||
78 | return result; | ||
79 | } | ||
80 | |||
81 | protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { | ||
82 | long x = tupleHash * impact; | ||
83 | objectCodeImpl.set(o, objectCodeImpl.get(o) + x); | ||
84 | } | ||
85 | |||
86 | protected long calculateModelCode(long lastSum) { | ||
87 | long result = 0; | ||
88 | for (var nullImpactValue : nullImpactValues) { | ||
89 | result = result * PRIME + Objects.hashCode(nullImpactValue.get(Tuple0.INSTANCE)); | ||
90 | } | ||
91 | result += lastSum; | ||
92 | return result; | ||
93 | } | ||
94 | } | ||
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 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.statecoding.neighbourhood; | ||
7 | |||
8 | import org.eclipse.collections.api.set.primitive.MutableLongSet; | ||
9 | import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; | ||
10 | import tools.refinery.store.model.Interpretation; | ||
11 | import tools.refinery.store.statecoding.StateCodeCalculator; | ||
12 | import tools.refinery.store.statecoding.StateCoderResult; | ||
13 | import tools.refinery.store.tuple.Tuple; | ||
14 | import tools.refinery.store.tuple.Tuple0; | ||
15 | |||
16 | import java.util.ArrayList; | ||
17 | import java.util.LinkedHashMap; | ||
18 | import java.util.List; | ||
19 | import java.util.Random; | ||
20 | |||
21 | public class CollectionNeighbourhoodCalculator implements StateCodeCalculator { | ||
22 | protected final List<Interpretation<?>> nullImpactValues; | ||
23 | protected final LinkedHashMap<Interpretation<?>, long[]> impactValues; | ||
24 | |||
25 | public CollectionNeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations) { | ||
26 | this.nullImpactValues = new ArrayList<>(); | ||
27 | this.impactValues = new LinkedHashMap<>(); | ||
28 | Random random = new Random(1); | ||
29 | |||
30 | for (Interpretation<?> interpretation : interpretations) { | ||
31 | int arity = interpretation.getSymbol().arity(); | ||
32 | if (arity == 0) { | ||
33 | nullImpactValues.add(interpretation); | ||
34 | } else { | ||
35 | long[] impact = new long[arity]; | ||
36 | for (int i = 0; i < arity; i++) { | ||
37 | impact[i] = random.nextLong(); | ||
38 | } | ||
39 | impactValues.put(interpretation, impact); | ||
40 | } | ||
41 | } | ||
42 | } | ||
43 | |||
44 | @Override | ||
45 | public StateCoderResult calculateCodes() { | ||
46 | ObjectCodeImpl previous = new ObjectCodeImpl(); | ||
47 | ObjectCodeImpl next = new ObjectCodeImpl(); | ||
48 | |||
49 | int previousSize = 1; | ||
50 | long lastSum; | ||
51 | boolean grows; | ||
52 | |||
53 | do{ | ||
54 | for (var impactValueEntry : this.impactValues.entrySet()) { | ||
55 | Interpretation<?> interpretation = impactValueEntry.getKey(); | ||
56 | long[] impact = impactValueEntry.getValue(); | ||
57 | var cursor = interpretation.getAll(); | ||
58 | while (cursor.move()) { | ||
59 | Tuple tuple = cursor.getKey(); | ||
60 | Object value = cursor.getValue(); | ||
61 | long tupleHash = getTupleHash(tuple, value, previous); | ||
62 | addHash(next, tuple, impact, tupleHash); | ||
63 | } | ||
64 | } | ||
65 | |||
66 | previous = next; | ||
67 | next = null; | ||
68 | lastSum = 0; | ||
69 | MutableLongSet codes = new LongHashSet(); | ||
70 | for (int i = 0; i < previous.getSize(); i++) { | ||
71 | long objectHash = previous.get(i); | ||
72 | codes.add(objectHash); | ||
73 | |||
74 | final long shifted1 = objectHash>>> 32; | ||
75 | final long shifted2 = objectHash << 32; | ||
76 | lastSum += shifted1 + shifted2; | ||
77 | } | ||
78 | int nextSize = codes.size(); | ||
79 | grows = previousSize < nextSize; | ||
80 | previousSize = nextSize; | ||
81 | |||
82 | if(grows) { | ||
83 | next = new ObjectCodeImpl(previous); | ||
84 | } | ||
85 | } while (grows); | ||
86 | |||
87 | long result = 1; | ||
88 | for (var nullImpactValue : nullImpactValues) { | ||
89 | result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); | ||
90 | } | ||
91 | result += lastSum; | ||
92 | |||
93 | return new StateCoderResult((int) result, previous); | ||
94 | } | ||
95 | |||
96 | protected long getTupleHash(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { | ||
97 | long result = value.hashCode(); | ||
98 | int arity = tuple.getSize(); | ||
99 | if (arity == 1) { | ||
100 | result = result * 31 + objectCodeImpl.get(tuple.get(0)); | ||
101 | } else if (arity == 2) { | ||
102 | result = result * 31 + objectCodeImpl.get(tuple.get(0)); | ||
103 | result = result * 31 + objectCodeImpl.get(tuple.get(1)); | ||
104 | if (tuple.get(0) == tuple.get(1)) { | ||
105 | result++; | ||
106 | } | ||
107 | } else if (arity > 2) { | ||
108 | for (int i = 0; i < arity; i++) { | ||
109 | result = result * 31 + objectCodeImpl.get(tuple.get(i)); | ||
110 | } | ||
111 | } | ||
112 | return result; | ||
113 | } | ||
114 | |||
115 | protected void addHash(ObjectCodeImpl objectCodeImpl, Tuple tuple, long[] impact, long tupleHashCode) { | ||
116 | if (tuple.getSize() == 1) { | ||
117 | addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); | ||
118 | } else if (tuple.getSize() == 2) { | ||
119 | addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); | ||
120 | addHash(objectCodeImpl, tuple.get(1), impact[1], tupleHashCode); | ||
121 | } else if (tuple.getSize() > 2) { | ||
122 | for (int i = 0; i < tuple.getSize(); i++) { | ||
123 | addHash(objectCodeImpl, tuple.get(i), impact[i], tupleHashCode); | ||
124 | } | ||
125 | } | ||
126 | } | ||
127 | |||
128 | protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { | ||
129 | objectCodeImpl.set(o, objectCodeImpl.get(o) + tupleHash * impact); | ||
130 | } | ||
131 | } | ||
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..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 | |||
@@ -6,76 +6,57 @@ | |||
6 | package tools.refinery.store.statecoding.neighbourhood; | 6 | package tools.refinery.store.statecoding.neighbourhood; |
7 | 7 | ||
8 | import org.eclipse.collections.api.map.primitive.LongIntMap; | 8 | import org.eclipse.collections.api.map.primitive.LongIntMap; |
9 | import org.eclipse.collections.api.set.primitive.IntSet; | ||
9 | import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; | 10 | import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; |
10 | import tools.refinery.store.map.Cursor; | 11 | import tools.refinery.store.map.Cursor; |
11 | import tools.refinery.store.model.Interpretation; | 12 | import tools.refinery.store.model.Interpretation; |
12 | import tools.refinery.store.statecoding.StateCodeCalculator; | 13 | import tools.refinery.store.statecoding.StateCodeCalculator; |
13 | import tools.refinery.store.statecoding.StateCoderResult; | 14 | import tools.refinery.store.statecoding.StateCoderResult; |
14 | import tools.refinery.store.tuple.Tuple; | 15 | import tools.refinery.store.tuple.Tuple; |
15 | import tools.refinery.store.tuple.Tuple0; | ||
16 | 16 | ||
17 | import java.util.ArrayList; | ||
18 | import java.util.LinkedHashMap; | ||
19 | import java.util.List; | 17 | import java.util.List; |
20 | import java.util.Random; | ||
21 | 18 | ||
22 | public class LazyNeighbourhoodCalculator implements StateCodeCalculator { | 19 | public class LazyNeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { |
23 | protected final List<Interpretation<?>> nullImpactValues; | 20 | public LazyNeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations, IntSet individuals) { |
24 | protected final LinkedHashMap<Interpretation<?>, long[]> impactValues; | 21 | super(interpretations, individuals); |
25 | |||
26 | public LazyNeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations) { | ||
27 | this.nullImpactValues = new ArrayList<>(); | ||
28 | this.impactValues = new LinkedHashMap<>(); | ||
29 | Random random = new Random(1); | ||
30 | |||
31 | for (Interpretation<?> interpretation : interpretations) { | ||
32 | int arity = interpretation.getSymbol().arity(); | ||
33 | if (arity == 0) { | ||
34 | nullImpactValues.add(interpretation); | ||
35 | } else { | ||
36 | long[] impact = new long[arity]; | ||
37 | for (int i = 0; i < arity; i++) { | ||
38 | impact[i] = random.nextInt(); | ||
39 | } | ||
40 | impactValues.put(interpretation, impact); | ||
41 | } | ||
42 | } | ||
43 | } | 22 | } |
44 | 23 | ||
45 | public StateCoderResult calculateCodes() { | 24 | public StateCoderResult calculateCodes() { |
46 | ObjectCodeImpl previous = new ObjectCodeImpl(); | 25 | ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); |
47 | ObjectCodeImpl next = new ObjectCodeImpl(); | 26 | LongIntHashMap prevHash2Amount = new LongIntHashMap(); |
48 | LongIntMap hash2Amount = new LongIntHashMap(); | ||
49 | 27 | ||
50 | long lastSum; | 28 | long lastSum; |
29 | // All hash code is 0, except to the individuals. | ||
51 | int lastSize = 1; | 30 | int lastSize = 1; |
52 | boolean grows; | 31 | boolean first = true; |
53 | 32 | ||
33 | boolean grows; | ||
34 | int rounds = 0; | ||
54 | do { | 35 | do { |
55 | constructNextObjectCodes(previous, next, hash2Amount); | 36 | final ObjectCodeImpl nextObjectCode; |
37 | if (first) { | ||
38 | nextObjectCode = new ObjectCodeImpl(); | ||
39 | initializeWithIndividuals(nextObjectCode); | ||
40 | } else { | ||
41 | nextObjectCode = new ObjectCodeImpl(previousObjectCode); | ||
42 | } | ||
43 | constructNextObjectCodes(previousObjectCode, nextObjectCode, prevHash2Amount); | ||
56 | 44 | ||
57 | LongIntHashMap nextHash2Amount = new LongIntHashMap(); | 45 | LongIntHashMap nextHash2Amount = new LongIntHashMap(); |
58 | lastSum = calculateLastSum(previous, next, hash2Amount, nextHash2Amount); | 46 | lastSum = calculateLastSum(previousObjectCode, nextObjectCode, prevHash2Amount, nextHash2Amount); |
59 | |||
60 | previous = next; | ||
61 | next = null; | ||
62 | 47 | ||
63 | int nextSize = nextHash2Amount.size(); | 48 | int nextSize = nextHash2Amount.size(); |
64 | grows = nextSize > lastSize; | 49 | grows = nextSize > lastSize; |
65 | lastSize = nextSize; | 50 | lastSize = nextSize; |
51 | first = false; | ||
66 | 52 | ||
67 | if (grows) { | 53 | previousObjectCode = nextObjectCode; |
68 | next = new ObjectCodeImpl(previous); | 54 | prevHash2Amount = nextHash2Amount; |
69 | } | 55 | } while (grows && rounds++ < 4/*&& lastSize < previousObjectCode.getSize()*/); |
70 | } while (grows); | ||
71 | 56 | ||
72 | long result = 1; | 57 | long result = calculateModelCode(lastSum); |
73 | for (var nullImpactValue : nullImpactValues) { | ||
74 | result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); | ||
75 | } | ||
76 | result += lastSum; | ||
77 | 58 | ||
78 | return new StateCoderResult((int) result, previous); | 59 | return new StateCoderResult((int) result, previousObjectCode); |
79 | } | 60 | } |
80 | 61 | ||
81 | private long calculateLastSum(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount, | 62 | private long calculateLastSum(ObjectCodeImpl previous, ObjectCodeImpl next, LongIntMap hash2Amount, |
@@ -96,7 +77,7 @@ public class LazyNeighbourhoodCalculator implements StateCodeCalculator { | |||
96 | final long shifted1 = hash >>> 8; | 77 | final long shifted1 = hash >>> 8; |
97 | final long shifted2 = hash << 8; | 78 | final long shifted2 = hash << 8; |
98 | final long shifted3 = hash >> 2; | 79 | final long shifted3 = hash >> 2; |
99 | lastSum += shifted1*shifted3 + shifted2; | 80 | lastSum += shifted1 * shifted3 + shifted2; |
100 | } | 81 | } |
101 | return lastSum; | 82 | return lastSum; |
102 | } | 83 | } |
@@ -126,7 +107,7 @@ public class LazyNeighbourhoodCalculator implements StateCodeCalculator { | |||
126 | 107 | ||
127 | private boolean isUnique(LongIntMap hash2Amount, ObjectCodeImpl objectCodeImpl, int object) { | 108 | private boolean isUnique(LongIntMap hash2Amount, ObjectCodeImpl objectCodeImpl, int object) { |
128 | final long hash = objectCodeImpl.get(object); | 109 | final long hash = objectCodeImpl.get(object); |
129 | if(hash == 0) { | 110 | if (hash == 0) { |
130 | return false; | 111 | return false; |
131 | } | 112 | } |
132 | final int amount = hash2Amount.get(hash); | 113 | final int amount = hash2Amount.get(hash); |
@@ -149,12 +130,12 @@ public class LazyNeighbourhoodCalculator implements StateCodeCalculator { | |||
149 | } | 130 | } |
150 | 131 | ||
151 | private void lazyImpactCalculation2(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { | 132 | private void lazyImpactCalculation2(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { |
152 | Tuple tuple = cursor.getKey(); | 133 | final Tuple tuple = cursor.getKey(); |
153 | int o1 = tuple.get(0); | 134 | final int o1 = tuple.get(0); |
154 | int o2 = tuple.get(1); | 135 | final int o2 = tuple.get(1); |
155 | 136 | ||
156 | boolean u1 = isUnique(hash2Amount, previous, o1); | 137 | final boolean u1 = isUnique(hash2Amount, previous, o1); |
157 | boolean u2 = isUnique(hash2Amount, previous, o2); | 138 | final boolean u2 = isUnique(hash2Amount, previous, o2); |
158 | 139 | ||
159 | if (u1 && u2) { | 140 | if (u1 && u2) { |
160 | next.ensureSize(o1); | 141 | next.ensureSize(o1); |
@@ -175,9 +156,9 @@ public class LazyNeighbourhoodCalculator implements StateCodeCalculator { | |||
175 | } | 156 | } |
176 | 157 | ||
177 | private void lazyImpactCalculationN(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { | 158 | private void lazyImpactCalculationN(LongIntMap hash2Amount, ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { |
178 | Tuple tuple = cursor.getKey(); | 159 | final Tuple tuple = cursor.getKey(); |
179 | 160 | ||
180 | boolean[] uniques = new boolean[tuple.getSize()]; | 161 | final boolean[] uniques = new boolean[tuple.getSize()]; |
181 | boolean allUnique = true; | 162 | boolean allUnique = true; |
182 | for (int i = 0; i < tuple.getSize(); i++) { | 163 | for (int i = 0; i < tuple.getSize(); i++) { |
183 | final boolean isUnique = isUnique(hash2Amount, previous, tuple.get(i)); | 164 | final boolean isUnique = isUnique(hash2Amount, previous, tuple.get(i)); |
@@ -204,32 +185,4 @@ public class LazyNeighbourhoodCalculator implements StateCodeCalculator { | |||
204 | } | 185 | } |
205 | } | 186 | } |
206 | 187 | ||
207 | private long getTupleHash1(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { | ||
208 | long result = value.hashCode(); | ||
209 | result = result * 31 + objectCodeImpl.get(tuple.get(0)); | ||
210 | return result; | ||
211 | } | ||
212 | |||
213 | private long getTupleHash2(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { | ||
214 | long result = value.hashCode(); | ||
215 | result = result * 31 + objectCodeImpl.get(tuple.get(0)); | ||
216 | result = result * 31 + objectCodeImpl.get(tuple.get(1)); | ||
217 | if (tuple.get(0) == tuple.get(1)) { | ||
218 | result*=31; | ||
219 | } | ||
220 | return result; | ||
221 | } | ||
222 | |||
223 | private long getTupleHashN(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { | ||
224 | long result = value.hashCode(); | ||
225 | for (int i = 0; i < tuple.getSize(); i++) { | ||
226 | result = result * 31 + objectCodeImpl.get(tuple.get(i)); | ||
227 | } | ||
228 | return result; | ||
229 | } | ||
230 | |||
231 | protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { | ||
232 | long x = tupleHash * impact; | ||
233 | objectCodeImpl.set(o, objectCodeImpl.get(o) + x); | ||
234 | } | ||
235 | } | 188 | } |
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 212291c3..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 | |||
@@ -5,128 +5,108 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.statecoding.neighbourhood; | 6 | package tools.refinery.store.statecoding.neighbourhood; |
7 | 7 | ||
8 | import org.eclipse.collections.api.set.primitive.MutableLongSet; | 8 | import org.eclipse.collections.api.set.primitive.IntSet; |
9 | import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; | 9 | import tools.refinery.store.map.Cursor; |
10 | import tools.refinery.store.model.Interpretation; | 10 | import tools.refinery.store.model.Interpretation; |
11 | import tools.refinery.store.statecoding.ObjectCode; | ||
11 | import tools.refinery.store.statecoding.StateCodeCalculator; | 12 | import tools.refinery.store.statecoding.StateCodeCalculator; |
12 | import tools.refinery.store.statecoding.StateCoderResult; | 13 | import tools.refinery.store.statecoding.StateCoderResult; |
13 | import tools.refinery.store.tuple.Tuple; | 14 | import tools.refinery.store.tuple.Tuple; |
14 | import tools.refinery.store.tuple.Tuple0; | 15 | import tools.refinery.store.tuple.Tuple0; |
15 | 16 | ||
16 | import java.util.ArrayList; | ||
17 | import java.util.LinkedHashMap; | ||
18 | import java.util.List; | 17 | import java.util.List; |
19 | import java.util.Random; | 18 | import java.util.Objects; |
20 | 19 | ||
21 | public class NeighbourhoodCalculator implements StateCodeCalculator { | 20 | public class NeighbourhoodCalculator extends AbstractNeighbourhoodCalculator implements StateCodeCalculator { |
22 | protected final List<Interpretation<?>> nullImpactValues; | 21 | public NeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations, IntSet individuals) { |
23 | protected final LinkedHashMap<Interpretation<?>, long[]> impactValues; | 22 | super(interpretations, individuals); |
24 | |||
25 | public NeighbourhoodCalculator(List<? extends Interpretation<?>> interpretations) { | ||
26 | this.nullImpactValues = new ArrayList<>(); | ||
27 | this.impactValues = new LinkedHashMap<>(); | ||
28 | Random random = new Random(1); | ||
29 | |||
30 | for (Interpretation<?> interpretation : interpretations) { | ||
31 | int arity = interpretation.getSymbol().arity(); | ||
32 | if (arity == 0) { | ||
33 | nullImpactValues.add(interpretation); | ||
34 | } else { | ||
35 | long[] impact = new long[arity]; | ||
36 | for (int i = 0; i < arity; i++) { | ||
37 | impact[i] = random.nextLong(); | ||
38 | } | ||
39 | impactValues.put(interpretation, impact); | ||
40 | } | ||
41 | } | ||
42 | } | 23 | } |
43 | 24 | ||
44 | @Override | ||
45 | public StateCoderResult calculateCodes() { | 25 | public StateCoderResult calculateCodes() { |
46 | ObjectCodeImpl previous = new ObjectCodeImpl(); | 26 | ObjectCodeImpl previousObjectCode = new ObjectCodeImpl(); |
47 | ObjectCodeImpl next = new ObjectCodeImpl(); | 27 | initializeWithIndividuals(previousObjectCode); |
48 | |||
49 | int previousSize = 1; | ||
50 | long lastSum; | ||
51 | boolean grows; | ||
52 | |||
53 | do{ | ||
54 | for (var impactValueEntry : this.impactValues.entrySet()) { | ||
55 | Interpretation<?> interpretation = impactValueEntry.getKey(); | ||
56 | long[] impact = impactValueEntry.getValue(); | ||
57 | var cursor = interpretation.getAll(); | ||
58 | while (cursor.move()) { | ||
59 | Tuple tuple = cursor.getKey(); | ||
60 | Object value = cursor.getValue(); | ||
61 | long tupleHash = getTupleHash(tuple, value, previous); | ||
62 | addHash(next, tuple, impact, tupleHash); | ||
63 | } | ||
64 | } | ||
65 | 28 | ||
66 | previous = next; | 29 | int rounds = 0; |
67 | next = null; | 30 | do { |
68 | lastSum = 0; | 31 | final ObjectCodeImpl nextObjectCode = rounds == 0 ? new ObjectCodeImpl() : |
69 | MutableLongSet codes = new LongHashSet(); | 32 | new ObjectCodeImpl(previousObjectCode.getSize()); |
70 | for (int i = 0; i < previous.getSize(); i++) { | ||
71 | long objectHash = previous.get(i); | ||
72 | codes.add(objectHash); | ||
73 | |||
74 | final long shifted1 = objectHash>>> 32; | ||
75 | final long shifted2 = objectHash << 32; | ||
76 | lastSum += shifted1 + shifted2; | ||
77 | } | ||
78 | int nextSize = codes.size(); | ||
79 | grows = previousSize < nextSize; | ||
80 | previousSize = nextSize; | ||
81 | 33 | ||
82 | if(grows) { | 34 | constructNextObjectCodes(previousObjectCode, nextObjectCode); |
83 | next = new ObjectCodeImpl(previous); | 35 | previousObjectCode = nextObjectCode; |
84 | } | 36 | rounds++; |
85 | } while (grows); | 37 | } while (rounds <= 7 && rounds <= previousObjectCode.getEffectiveSize()); |
86 | 38 | ||
87 | long result = 1; | 39 | long result = calculateLastSum(previousObjectCode); |
40 | return new StateCoderResult((int) result, previousObjectCode); | ||
41 | } | ||
42 | |||
43 | private long calculateLastSum(ObjectCode codes) { | ||
44 | long result = 0; | ||
88 | for (var nullImpactValue : nullImpactValues) { | 45 | for (var nullImpactValue : nullImpactValues) { |
89 | result = result * 31 + nullImpactValue.get(Tuple0.INSTANCE).hashCode(); | 46 | result = result * 31 + Objects.hashCode(nullImpactValue.get(Tuple0.INSTANCE)); |
90 | } | 47 | } |
91 | result += lastSum; | ||
92 | 48 | ||
93 | return new StateCoderResult((int) result, previous); | 49 | for (int i = 0; i < codes.getSize(); i++) { |
94 | } | 50 | final long hash = codes.get(i); |
95 | 51 | result += hash*PRIME; | |
96 | protected long getTupleHash(Tuple tuple, Object value, ObjectCodeImpl objectCodeImpl) { | ||
97 | long result = value.hashCode(); | ||
98 | int arity = tuple.getSize(); | ||
99 | if (arity == 1) { | ||
100 | result = result * 31 + objectCodeImpl.get(tuple.get(0)); | ||
101 | } else if (arity == 2) { | ||
102 | result = result * 31 + objectCodeImpl.get(tuple.get(0)); | ||
103 | result = result * 31 + objectCodeImpl.get(tuple.get(1)); | ||
104 | if (tuple.get(0) == tuple.get(1)) { | ||
105 | result++; | ||
106 | } | ||
107 | } else if (arity > 2) { | ||
108 | for (int i = 0; i < arity; i++) { | ||
109 | result = result * 31 + objectCodeImpl.get(tuple.get(i)); | ||
110 | } | ||
111 | } | 52 | } |
53 | |||
112 | return result; | 54 | return result; |
113 | } | 55 | } |
114 | 56 | ||
115 | protected void addHash(ObjectCodeImpl objectCodeImpl, Tuple tuple, long[] impact, long tupleHashCode) { | 57 | private void constructNextObjectCodes(ObjectCodeImpl previous, ObjectCodeImpl next) { |
116 | if (tuple.getSize() == 1) { | 58 | for (var impactValueEntry : this.impactValues.entrySet()) { |
117 | addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); | 59 | Interpretation<?> interpretation = impactValueEntry.getKey(); |
118 | } else if (tuple.getSize() == 2) { | 60 | var cursor = interpretation.getAll(); |
119 | addHash(objectCodeImpl, tuple.get(0), impact[0], tupleHashCode); | 61 | int arity = interpretation.getSymbol().arity(); |
120 | addHash(objectCodeImpl, tuple.get(1), impact[1], tupleHashCode); | 62 | long[] impactValue = impactValueEntry.getValue(); |
121 | } else if (tuple.getSize() > 2) { | 63 | |
122 | for (int i = 0; i < tuple.getSize(); i++) { | 64 | if (arity == 1) { |
123 | addHash(objectCodeImpl, tuple.get(i), impact[i], tupleHashCode); | 65 | while (cursor.move()) { |
66 | impactCalculation1(previous, next, impactValue, cursor); | ||
67 | } | ||
68 | } else if (arity == 2) { | ||
69 | while (cursor.move()) { | ||
70 | impactCalculation2(previous, next, impactValue, cursor); | ||
71 | } | ||
72 | } else { | ||
73 | while (cursor.move()) { | ||
74 | impactCalculationN(previous, next, impactValue, cursor); | ||
75 | } | ||
124 | } | 76 | } |
125 | } | 77 | } |
126 | } | 78 | } |
127 | 79 | ||
128 | protected void addHash(ObjectCodeImpl objectCodeImpl, int o, long impact, long tupleHash) { | 80 | |
129 | objectCodeImpl.set(o, objectCodeImpl.get(o) + tupleHash * impact); | 81 | private void impactCalculation1(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { |
82 | |||
83 | Tuple tuple = cursor.getKey(); | ||
84 | int o = tuple.get(0); | ||
85 | Object value = cursor.getValue(); | ||
86 | long tupleHash = getTupleHash1(tuple, value, previous); | ||
87 | addHash(next, o, impactValues[0], tupleHash); | ||
88 | } | ||
89 | |||
90 | private void impactCalculation2(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { | ||
91 | final Tuple tuple = cursor.getKey(); | ||
92 | final int o1 = tuple.get(0); | ||
93 | final int o2 = tuple.get(1); | ||
94 | |||
95 | Object value = cursor.getValue(); | ||
96 | long tupleHash = getTupleHash2(tuple, value, previous); | ||
97 | |||
98 | addHash(next, o1, impactValues[0], tupleHash); | ||
99 | addHash(next, o2, impactValues[1], tupleHash); | ||
130 | } | 100 | } |
131 | 101 | ||
102 | private void impactCalculationN(ObjectCodeImpl previous, ObjectCodeImpl next, long[] impactValues, Cursor<Tuple, ?> cursor) { | ||
103 | final Tuple tuple = cursor.getKey(); | ||
104 | |||
105 | Object value = cursor.getValue(); | ||
106 | long tupleHash = getTupleHashN(tuple, value, previous); | ||
107 | |||
108 | for (int i = 0; i < tuple.getSize(); i++) { | ||
109 | addHash(next, tuple.get(i), impactValues[i], tupleHash); | ||
110 | } | ||
111 | } | ||
132 | } | 112 | } |
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..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,26 +12,35 @@ import java.util.Arrays; | |||
12 | public class ObjectCodeImpl implements ObjectCode { | 12 | public class ObjectCodeImpl implements ObjectCode { |
13 | private long[] vector; | 13 | private long[] vector; |
14 | private int size; | 14 | private int size; |
15 | private int effectiveSize; | ||
15 | 16 | ||
16 | public ObjectCodeImpl() { | 17 | public ObjectCodeImpl() { |
17 | vector = new long[10]; | 18 | vector = new long[10]; |
18 | size = 0; | 19 | size = 0; |
20 | effectiveSize = 0; | ||
19 | } | 21 | } |
20 | 22 | ||
21 | public ObjectCodeImpl(ObjectCodeImpl sameSize) { | 23 | public ObjectCodeImpl(int size) { |
22 | this.vector = new long[sameSize.size]; | 24 | this.vector = new long[size]; |
23 | this.size = sameSize.size; | 25 | this.size = size; |
26 | effectiveSize = 0; | ||
27 | } | ||
28 | |||
29 | public ObjectCodeImpl(ObjectCodeImpl copy) { | ||
30 | this.vector = Arrays.copyOf(copy.vector, copy.size); | ||
31 | this.size = copy.size; | ||
32 | effectiveSize = copy.effectiveSize; | ||
24 | } | 33 | } |
25 | 34 | ||
26 | public void ensureSize(int object) { | 35 | public void ensureSize(int object) { |
27 | if(object >= size) { | 36 | if (object >= size) { |
28 | size = object+1; | 37 | size = object + 1; |
29 | } | 38 | } |
30 | 39 | ||
31 | if(object >= vector.length) { | 40 | if (object >= vector.length) { |
32 | int newLength = vector.length*2; | 41 | int newLength = vector.length * 2; |
33 | while(object >= newLength) { | 42 | while (object >= newLength) { |
34 | newLength*=2; | 43 | newLength *= 2; |
35 | } | 44 | } |
36 | 45 | ||
37 | long[] newVector = new long[newLength]; | 46 | long[] newVector = new long[newLength]; |
@@ -41,7 +50,7 @@ public class ObjectCodeImpl implements ObjectCode { | |||
41 | } | 50 | } |
42 | 51 | ||
43 | public long get(int object) { | 52 | public long get(int object) { |
44 | if(object < vector.length) { | 53 | if (object < vector.length) { |
45 | return vector[object]; | 54 | return vector[object]; |
46 | } else { | 55 | } else { |
47 | return 0; | 56 | return 0; |
@@ -50,17 +59,23 @@ public class ObjectCodeImpl implements ObjectCode { | |||
50 | 59 | ||
51 | public void set(int object, long value) { | 60 | public void set(int object, long value) { |
52 | ensureSize(object); | 61 | ensureSize(object); |
53 | vector[object]=value; | 62 | final long valueToPut = value == 0 ? 1 : value; |
63 | if (vector[object] == 0) effectiveSize++; | ||
64 | vector[object] = valueToPut; | ||
54 | } | 65 | } |
55 | 66 | ||
56 | public int getSize() { | 67 | public int getSize() { |
57 | return this.size; | 68 | return this.size; |
58 | } | 69 | } |
59 | 70 | ||
71 | public int getEffectiveSize() { | ||
72 | return this.effectiveSize; | ||
73 | } | ||
74 | |||
60 | @Override | 75 | @Override |
61 | public String toString() { | 76 | public String toString() { |
62 | return "ObjectCodeImpl{" + | 77 | return "ObjectCodeImpl{" + |
63 | "vector=" + Arrays.toString(Arrays.copyOf(vector,this.size)) + | 78 | "vector=" + Arrays.toString(Arrays.copyOf(vector, this.size)) + |
64 | '}'; | 79 | '}'; |
65 | } | 80 | } |
66 | } | 81 | } |
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 { | |||
57 | for (var permutation : previousPermutations) { | 57 | for (var permutation : previousPermutations) { |
58 | for (int pos = 0; pos <= max; pos++) { | 58 | for (int pos = 0; pos <= max; pos++) { |
59 | int[] newPermutation = new int[max + 1]; | 59 | int[] newPermutation = new int[max + 1]; |
60 | if (pos >= 0) | 60 | System.arraycopy(permutation, 0, newPermutation, 0, pos); |
61 | System.arraycopy(permutation, 0, newPermutation, 0, pos); | ||
62 | newPermutation[pos] = max; | 61 | newPermutation[pos] = max; |
63 | if (max - (pos + 1) >= 0) | 62 | if (max - (pos + 1) >= 0) |
64 | System.arraycopy(permutation, pos + 1, newPermutation, pos + 1 + 1, max - (pos + 1)); | 63 | System.arraycopy(permutation, pos + 1, newPermutation, pos + 1 + 1, max - (pos + 1)); |
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..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 | |||
@@ -5,7 +5,9 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.statecoding.stateequivalence; | 6 | package tools.refinery.store.statecoding.stateequivalence; |
7 | 7 | ||
8 | import org.eclipse.collections.api.factory.primitive.IntIntMaps; | ||
8 | import org.eclipse.collections.api.map.primitive.IntIntMap; | 9 | import org.eclipse.collections.api.map.primitive.IntIntMap; |
10 | import org.eclipse.collections.api.set.primitive.IntSet; | ||
9 | import org.eclipse.collections.impl.map.mutable.primitive.IntIntHashMap; | 11 | import org.eclipse.collections.impl.map.mutable.primitive.IntIntHashMap; |
10 | import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; | 12 | import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; |
11 | import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; | 13 | import org.eclipse.collections.impl.set.mutable.primitive.IntHashSet; |
@@ -23,18 +25,17 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { | |||
23 | public static final int LIMIT = 1000; | 25 | public static final int LIMIT = 1000; |
24 | 26 | ||
25 | @Override | 27 | @Override |
26 | public EquivalenceResult constructMorphism(List<? extends Interpretation<?>> interpretations1, | 28 | public EquivalenceResult constructMorphism(IntSet individuals, |
29 | List<? extends Interpretation<?>> interpretations1, | ||
27 | ObjectCode code1, | 30 | ObjectCode code1, |
28 | List<? extends Interpretation<?>> interpretations2, | 31 | List<? extends Interpretation<?>> interpretations2, |
29 | ObjectCode code2) { | 32 | ObjectCode code2) |
30 | if (code1.getSize() != code2.getSize()) { | 33 | { |
31 | return EquivalenceResult.DIFFERENT; | ||
32 | } | ||
33 | |||
34 | IntIntHashMap object2PermutationGroup = new IntIntHashMap(); | 34 | IntIntHashMap object2PermutationGroup = new IntIntHashMap(); |
35 | List<List<IntIntMap>> permutationsGroups = new ArrayList<>(); | 35 | List<List<IntIntMap>> permutationsGroups = new ArrayList<>(); |
36 | 36 | ||
37 | final EquivalenceResult permutations = constructPermutationNavigation(indexByHash(code1), indexByHash(code2), | 37 | final EquivalenceResult permutations = constructPermutationNavigation(individuals, |
38 | indexByHash(code1, individuals), indexByHash(code2, individuals), | ||
38 | object2PermutationGroup, permutationsGroups); | 39 | object2PermutationGroup, permutationsGroups); |
39 | 40 | ||
40 | if (permutations == EquivalenceResult.DIFFERENT) { | 41 | if (permutations == EquivalenceResult.DIFFERENT) { |
@@ -57,27 +58,36 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { | |||
57 | tried++; | 58 | tried++; |
58 | } while (hasNext); | 59 | } while (hasNext); |
59 | 60 | ||
60 | return EquivalenceResult.DIFFERENT; | 61 | if(permutations == EquivalenceResult.UNKNOWN) { |
62 | return EquivalenceResult.UNKNOWN; | ||
63 | } else { | ||
64 | return EquivalenceResult.DIFFERENT; | ||
65 | } | ||
61 | } | 66 | } |
62 | 67 | ||
63 | private LongObjectHashMap<IntHashSet> indexByHash(ObjectCode code) { | 68 | private LongObjectHashMap<IntHashSet> indexByHash(ObjectCode code, IntSet individuals) { |
64 | LongObjectHashMap<IntHashSet> result = new LongObjectHashMap<>(); | 69 | LongObjectHashMap<IntHashSet> result = new LongObjectHashMap<>(); |
65 | for (int o = 0; o < code.getSize(); o++) { | 70 | for (int o = 0; o < code.getSize(); o++) { |
66 | long hash = code.get(o); | 71 | if(! individuals.contains(o)){ |
67 | var equivalenceClass = result.get(hash); | 72 | long hash = code.get(o); |
68 | if (equivalenceClass == null) { | 73 | if(hash != 0) { |
69 | equivalenceClass = new IntHashSet(); | 74 | var equivalenceClass = result.get(hash); |
70 | result.put(hash, equivalenceClass); | 75 | if (equivalenceClass == null) { |
76 | equivalenceClass = new IntHashSet(); | ||
77 | result.put(hash, equivalenceClass); | ||
78 | } | ||
79 | equivalenceClass.add(o); | ||
80 | } | ||
71 | } | 81 | } |
72 | equivalenceClass.add(o); | ||
73 | } | 82 | } |
74 | return result; | 83 | return result; |
75 | } | 84 | } |
76 | 85 | ||
77 | private EquivalenceResult constructPermutationNavigation(LongObjectHashMap<IntHashSet> map1, | 86 | private EquivalenceResult constructPermutationNavigation(IntSet individuals, |
78 | LongObjectHashMap<IntHashSet> map2, | 87 | LongObjectHashMap<IntHashSet> map1, |
79 | IntIntHashMap emptyMapToListOfOptions, | 88 | LongObjectHashMap<IntHashSet> map2, |
80 | List<List<IntIntMap>> emptyListOfOptions) { | 89 | IntIntHashMap object2OptionIndex, |
90 | List<List<IntIntMap>> listOfOptions) { | ||
81 | if (map1.size() != map2.size()) { | 91 | if (map1.size() != map2.size()) { |
82 | return EquivalenceResult.DIFFERENT; | 92 | return EquivalenceResult.DIFFERENT; |
83 | } | 93 | } |
@@ -101,10 +111,13 @@ public class StateEquivalenceCheckerImpl implements StateEquivalenceChecker { | |||
101 | 111 | ||
102 | allComplete &= pairing.isComplete(); | 112 | allComplete &= pairing.isComplete(); |
103 | 113 | ||
104 | final int optionIndex = emptyListOfOptions.size(); | 114 | final int optionIndex = listOfOptions.size(); |
105 | set1.forEach(key -> emptyMapToListOfOptions.put(key, optionIndex)); | 115 | set1.forEach(key -> object2OptionIndex.put(key, optionIndex)); |
106 | emptyListOfOptions.add(pairing.permutations()); | 116 | listOfOptions.add(pairing.permutations()); |
107 | } | 117 | } |
118 | |||
119 | individuals.forEach(o -> listOfOptions.add(o,List.of(IntIntMaps.immutable.of(o,o)))); | ||
120 | |||
108 | if(allComplete) { | 121 | if(allComplete) { |
109 | return EquivalenceResult.ISOMORPHIC; | 122 | return EquivalenceResult.ISOMORPHIC; |
110 | } else { | 123 | } else { |
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..3c35849e --- /dev/null +++ b/subprojects/store/src/test/java/tools/refinery/store/statecoding/EquivalenceTest.java | |||
@@ -0,0 +1,217 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.statecoding; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.store.map.Version; | ||
10 | import tools.refinery.store.model.Model; | ||
11 | import tools.refinery.store.model.ModelStore; | ||
12 | import tools.refinery.store.representation.Symbol; | ||
13 | import tools.refinery.store.statecoding.neighbourhood.ObjectCodeImpl; | ||
14 | import tools.refinery.store.tuple.Tuple; | ||
15 | |||
16 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
17 | |||
18 | class EquivalenceTest { | ||
19 | Symbol<Boolean> person = new Symbol<>("Person", 1, Boolean.class, false); | ||
20 | Symbol<Integer> age = new Symbol<>("age", 1, Integer.class, null); | ||
21 | Symbol<Boolean> friend = new Symbol<>("friend", 2, Boolean.class, false); | ||
22 | Symbol<Boolean> parents = new Symbol<>("parents", 3, Boolean.class, false); | ||
23 | Symbol<Integer> population = new Symbol<>("population", 0, Integer.class, 0); | ||
24 | |||
25 | private ModelStore createStore() { | ||
26 | return ModelStore.builder() | ||
27 | .symbols(person, age, friend, parents, population) | ||
28 | .with(StateCoderAdapter.builder()) | ||
29 | .build(); | ||
30 | } | ||
31 | |||
32 | @Test | ||
33 | void emptyModelCode0() { | ||
34 | ModelStore store = createStore(); | ||
35 | var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); | ||
36 | Model model = createStore().createEmptyModel(); | ||
37 | Version v1 = model.commit(); | ||
38 | Version v2 = model.commit(); | ||
39 | |||
40 | assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v1, v2)); | ||
41 | |||
42 | var personI = model.getInterpretation(person); | ||
43 | var friendI = model.getInterpretation(friend); | ||
44 | |||
45 | personI.put(Tuple.of(1), true); | ||
46 | personI.put(Tuple.of(2), true); | ||
47 | friendI.put(Tuple.of(1, 2), true); | ||
48 | |||
49 | Version v3 = model.commit(); | ||
50 | |||
51 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v3)); | ||
52 | } | ||
53 | |||
54 | @Test | ||
55 | void nullRelationTest() { | ||
56 | ModelStore store = createStore(); | ||
57 | var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); | ||
58 | Model model = createStore().createEmptyModel(); | ||
59 | |||
60 | var populationI = model.getInterpretation(population); | ||
61 | |||
62 | Version v1 = model.commit(); | ||
63 | |||
64 | populationI.put(Tuple.of(), 1); | ||
65 | Version v2 = model.commit(); | ||
66 | |||
67 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v2)); | ||
68 | |||
69 | populationI.put(Tuple.of(), 2); | ||
70 | Version v3 = model.commit(); | ||
71 | |||
72 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v2, v3)); | ||
73 | } | ||
74 | |||
75 | @Test | ||
76 | void unaryBooleanTest() { | ||
77 | ModelStore store = createStore(); | ||
78 | var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); | ||
79 | Model model = createStore().createEmptyModel(); | ||
80 | |||
81 | var personI = model.getInterpretation(person); | ||
82 | |||
83 | Version v1 = model.commit(); | ||
84 | |||
85 | personI.put(Tuple.of(1), true); | ||
86 | Version v2 = model.commit(); | ||
87 | |||
88 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v2)); | ||
89 | |||
90 | personI.put(Tuple.of(2), true); | ||
91 | Version v3 = model.commit(); | ||
92 | |||
93 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v2, v3)); | ||
94 | |||
95 | personI.put(Tuple.of(1), false); | ||
96 | Version v4 = model.commit(); | ||
97 | |||
98 | assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v2, v4)); | ||
99 | } | ||
100 | |||
101 | @Test | ||
102 | void unaryIntTest() { | ||
103 | ModelStore store = createStore(); | ||
104 | var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); | ||
105 | Model model = createStore().createEmptyModel(); | ||
106 | |||
107 | var ageI = model.getInterpretation(age); | ||
108 | |||
109 | ageI.put(Tuple.of(1), 3); | ||
110 | Version v1 = model.commit(); | ||
111 | |||
112 | ageI.put(Tuple.of(1), 4); | ||
113 | Version v2 = model.commit(); | ||
114 | |||
115 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v2)); | ||
116 | |||
117 | ageI.put(Tuple.of(2), 4); | ||
118 | Version v3 = model.commit(); | ||
119 | |||
120 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v2, v3)); | ||
121 | |||
122 | ageI.put(Tuple.of(1), null); | ||
123 | Version v4 = model.commit(); | ||
124 | |||
125 | assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v2, v4)); | ||
126 | } | ||
127 | |||
128 | @Test | ||
129 | void binaryTest() { | ||
130 | ModelStore store = createStore(); | ||
131 | var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); | ||
132 | Model model = createStore().createEmptyModel(); | ||
133 | |||
134 | var friendI = model.getInterpretation(friend); | ||
135 | |||
136 | Version v1 = model.commit(); | ||
137 | |||
138 | friendI.put(Tuple.of(1, 2), true); | ||
139 | Version v2 = model.commit(); | ||
140 | |||
141 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v2)); | ||
142 | |||
143 | friendI.put(Tuple.of(2, 1), true); | ||
144 | Version v3 = model.commit(); | ||
145 | |||
146 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v2, v3)); | ||
147 | |||
148 | friendI.put(Tuple.of(1, 2), false); | ||
149 | Version v4 = model.commit(); | ||
150 | |||
151 | assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v2, v4)); | ||
152 | } | ||
153 | |||
154 | @Test | ||
155 | void NaryTest() { | ||
156 | ModelStore store = createStore(); | ||
157 | var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); | ||
158 | Model model = createStore().createEmptyModel(); | ||
159 | |||
160 | var parentsI = model.getInterpretation(parents); | ||
161 | |||
162 | Version v1 = model.commit(); | ||
163 | |||
164 | parentsI.put(Tuple.of(3, 1, 2), true); | ||
165 | Version v2 = model.commit(); | ||
166 | |||
167 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v1, v2)); | ||
168 | |||
169 | parentsI.put(Tuple.of(4, 1, 2), true); | ||
170 | Version v3 = model.commit(); | ||
171 | |||
172 | assertEquals(StateEquivalenceChecker.EquivalenceResult.DIFFERENT, stateCoder.checkEquivalence(v2, v3)); | ||
173 | |||
174 | parentsI.put(Tuple.of(3, 1, 2), false); | ||
175 | Version v4 = model.commit(); | ||
176 | |||
177 | assertEquals(StateEquivalenceChecker.EquivalenceResult.ISOMORPHIC, stateCoder.checkEquivalence(v2, v4)); | ||
178 | } | ||
179 | |||
180 | @Test | ||
181 | void largeUnknownTest() { | ||
182 | final int limit = 100; | ||
183 | |||
184 | StateCodeCalculator calculator = () -> { | ||
185 | var code = new ObjectCodeImpl(); | ||
186 | for (int i = 0; i < limit; i++) { | ||
187 | code.set(i, 1); | ||
188 | } | ||
189 | return new StateCoderResult(1, code); | ||
190 | }; | ||
191 | |||
192 | ModelStore store = ModelStore.builder() | ||
193 | .symbols(person, age, friend, parents, population) | ||
194 | .with(StateCoderAdapter.builder() | ||
195 | .stateCodeCalculatorFactory((p1, p2) -> calculator)) | ||
196 | .build(); | ||
197 | |||
198 | var stateCoder = store.getAdapter(StateCoderStoreAdapter.class); | ||
199 | Model model = createStore().createEmptyModel(); | ||
200 | |||
201 | var personI = model.getInterpretation(person); | ||
202 | var friendI = model.getInterpretation(friend); | ||
203 | |||
204 | for (int i = 0; i < limit; i++) { | ||
205 | personI.put(Tuple.of(i), true); | ||
206 | } | ||
207 | |||
208 | friendI.put(Tuple.of(11,12),true); | ||
209 | var v1 = model.commit(); | ||
210 | |||
211 | friendI.put(Tuple.of(11,12),false); | ||
212 | friendI.put(Tuple.of(21,22),false); | ||
213 | var v2 = model.commit(); | ||
214 | |||
215 | assertEquals(StateEquivalenceChecker.EquivalenceResult.UNKNOWN, stateCoder.checkEquivalence(v1,v2)); | ||
216 | } | ||
217 | } | ||
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 @@ | |||
5 | */ | 5 | */ |
6 | package tools.refinery.store.statecoding; | 6 | package tools.refinery.store.statecoding; |
7 | 7 | ||
8 | import org.eclipse.collections.impl.map.mutable.primitive.IntObjectHashMap; | ||
9 | import org.junit.jupiter.api.Tag; | ||
8 | import org.junit.jupiter.api.Test; | 10 | import org.junit.jupiter.api.Test; |
11 | import org.junit.jupiter.params.ParameterizedTest; | ||
12 | import org.junit.jupiter.params.provider.ValueSource; | ||
9 | import tools.refinery.store.map.Version; | 13 | import tools.refinery.store.map.Version; |
14 | import tools.refinery.store.model.Model; | ||
10 | import tools.refinery.store.model.ModelStore; | 15 | import tools.refinery.store.model.ModelStore; |
11 | import tools.refinery.store.representation.Symbol; | 16 | import tools.refinery.store.representation.Symbol; |
12 | import tools.refinery.store.tuple.Tuple; | 17 | import tools.refinery.store.tuple.Tuple; |
13 | 18 | ||
14 | import java.util.*; | 19 | import java.util.ArrayList; |
20 | import java.util.HashSet; | ||
21 | import java.util.List; | ||
22 | import java.util.Set; | ||
23 | |||
24 | import static org.junit.jupiter.api.Assertions.assertEquals; | ||
25 | import static org.junit.jupiter.api.Assertions.assertTrue; | ||
26 | |||
15 | 27 | ||
16 | class ExperimentalSetupTest { | 28 | class ExperimentalSetupTest { |
17 | public static void generate(int size) { | 29 | static class ExperimentalSetupResult { |
30 | int versions = 0; | ||
31 | int different = 0; | ||
32 | int isomorphic = 0; | ||
33 | int unknown = 0; | ||
34 | |||
35 | double failureRatio() { | ||
36 | return (different + 0.0) / versions; | ||
37 | } | ||
38 | |||
39 | @Override | ||
40 | public String toString() { | ||
41 | return "ExperimentalSetupResult{" + | ||
42 | "versions=" + versions + | ||
43 | ", different=" + different + | ||
44 | ", isomorphic=" + isomorphic + | ||
45 | ", unknown=" + unknown + | ||
46 | ", ratio= " + failureRatio() + | ||
47 | '}'; | ||
48 | } | ||
49 | } | ||
50 | |||
51 | static int MAX = 100000; | ||
52 | |||
53 | public static ExperimentalSetupResult generate(int size, boolean permuteTypes) { | ||
18 | Symbol<Boolean> person = new Symbol<>("Person", 1, Boolean.class, false); | 54 | Symbol<Boolean> person = new Symbol<>("Person", 1, Boolean.class, false); |
19 | Symbol<Boolean> friend = new Symbol<>("friend", 2, Boolean.class, false); | 55 | Symbol<Boolean> friend = new Symbol<>("friend", 2, Boolean.class, false); |
20 | 56 | ||
@@ -25,13 +61,13 @@ class ExperimentalSetupTest { | |||
25 | .build(); | 61 | .build(); |
26 | 62 | ||
27 | Set<Version> versions = new HashSet<>(); | 63 | Set<Version> versions = new HashSet<>(); |
28 | Map<Integer, List<Version>> codes = new HashMap<>(); | 64 | IntObjectHashMap<List<Version>> codes = new IntObjectHashMap<>(); |
29 | 65 | ||
30 | var empty = store.createEmptyModel(); | 66 | var empty = store.createEmptyModel(); |
31 | var pI = empty.getInterpretation(person); | 67 | if (!permuteTypes) { |
32 | 68 | for (int i = 0; i < size; i++) { | |
33 | for (int i = 0; i < size; i++) { | 69 | empty.getInterpretation(person).put(Tuple.of(i), true); |
34 | pI.put(Tuple.of(i), true); | 70 | } |
35 | } | 71 | } |
36 | 72 | ||
37 | var emptyVersion = empty.commit(); | 73 | var emptyVersion = empty.commit(); |
@@ -42,12 +78,27 @@ class ExperimentalSetupTest { | |||
42 | codes.put(emptyCode, emptyList); | 78 | codes.put(emptyCode, emptyList); |
43 | 79 | ||
44 | var storeAdapter = store.getAdapter(StateCoderStoreAdapter.class); | 80 | var storeAdapter = store.getAdapter(StateCoderStoreAdapter.class); |
81 | var result = new ExperimentalSetupResult(); | ||
45 | 82 | ||
46 | int dif = 0; | 83 | int steps = 0; |
47 | int iso = 0; | ||
48 | int unk = 0; | ||
49 | 84 | ||
50 | //int step = 0 | 85 | if (permuteTypes) { |
86 | for (int i = 0; i < size; i++) { | ||
87 | var previousVersions = new HashSet<>(versions); | ||
88 | for (var version : previousVersions) { | ||
89 | var model = store.createModelForState(version); | ||
90 | model.getInterpretation(person).put(Tuple.of(i), true); | ||
91 | |||
92 | saveAsNewVersion(versions, codes, storeAdapter, result, model); | ||
93 | |||
94 | logProgress(steps++); | ||
95 | if (steps > MAX) { | ||
96 | result.versions = versions.size(); | ||
97 | return result; | ||
98 | } | ||
99 | } | ||
100 | } | ||
101 | } | ||
51 | 102 | ||
52 | for (int i = 0; i < size; i++) { | 103 | for (int i = 0; i < size; i++) { |
53 | for (int j = 0; j < size; j++) { | 104 | for (int j = 0; j < size; j++) { |
@@ -57,51 +108,91 @@ class ExperimentalSetupTest { | |||
57 | var model = store.createModelForState(version); | 108 | var model = store.createModelForState(version); |
58 | model.getInterpretation(friend).put(Tuple.of(i, j), true); | 109 | model.getInterpretation(friend).put(Tuple.of(i, j), true); |
59 | 110 | ||
60 | Version version1 = model.commit(); | 111 | saveAsNewVersion(versions, codes, storeAdapter, result, model); |
61 | var stateCode = model.getAdapter(StateCoderAdapter.class).calculateStateCode(); | 112 | |
62 | int code = stateCode.modelCode(); | 113 | logProgress(steps++); |
63 | //System.out.println(step+++" ->" +code); | 114 | if (steps > MAX) { |
64 | if (codes.containsKey(code)) { | 115 | result.versions = versions.size(); |
65 | Version similar = codes.get(code).get(0); | 116 | return result; |
66 | |||
67 | var outcome = storeAdapter.checkEquivalence(version1, similar); | ||
68 | if (outcome == StateEquivalenceChecker.EquivalenceResult.DIFFERENT) { | ||
69 | System.out.println(); | ||
70 | var c = model.getInterpretation(friend).getAll(); | ||
71 | while (c.move()) { | ||
72 | System.out.println(c.getKey().toString()); | ||
73 | } | ||
74 | System.out.println("vs"); | ||
75 | var c2 = store.createModelForState(similar).getInterpretation(friend).getAll(); | ||
76 | while (c2.move()) { | ||
77 | System.out.println(c2.getKey().toString()); | ||
78 | } | ||
79 | |||
80 | dif++; | ||
81 | } else if (outcome == StateEquivalenceChecker.EquivalenceResult.UNKNOWN) { | ||
82 | unk++; | ||
83 | } else { | ||
84 | iso++; | ||
85 | } | ||
86 | } else { | ||
87 | versions.add(version1); | ||
88 | |||
89 | List<Version> newList = new ArrayList<>(); | ||
90 | newList.add(version1); | ||
91 | codes.put(code, newList); | ||
92 | } | 117 | } |
93 | } | 118 | } |
94 | } | 119 | } |
95 | } | 120 | } |
96 | 121 | ||
97 | System.out.printf("v=%d i=%d d=%d u=%d\n", versions.size(), iso, dif, unk); | 122 | result.versions = versions.size(); |
123 | return result; | ||
98 | } | 124 | } |
99 | 125 | ||
100 | @Test | 126 | private static void saveAsNewVersion(Set<Version> versions, IntObjectHashMap<List<Version>> codes, |
101 | void runTests() { | 127 | StateCoderStoreAdapter storeAdapter, ExperimentalSetupResult result, Model model) { |
102 | for (int i = 0; i < 5; i++) { | 128 | Version version1 = model.commit(); |
103 | System.out.println("size = " + i); | 129 | |
104 | generate(i); | 130 | var stateCode = model.getAdapter(StateCoderAdapter.class).calculateStateCode(); |
131 | int code = stateCode.modelCode(); | ||
132 | if (codes.containsKey(code)) { | ||
133 | Version similar = codes.get(code).get(0); | ||
134 | |||
135 | var outcome = storeAdapter.checkEquivalence(version1, similar); | ||
136 | if (outcome == StateEquivalenceChecker.EquivalenceResult.DIFFERENT) { | ||
137 | result.different++; | ||
138 | } else if (outcome == StateEquivalenceChecker.EquivalenceResult.UNKNOWN) { | ||
139 | result.unknown++; | ||
140 | } else { | ||
141 | result.isomorphic++; | ||
142 | } | ||
143 | } else { | ||
144 | versions.add(version1); | ||
145 | |||
146 | List<Version> newList = new ArrayList<>(); | ||
147 | newList.add(version1); | ||
148 | codes.put(code, newList); | ||
149 | } | ||
150 | } | ||
151 | |||
152 | private static void logProgress(int steps) { | ||
153 | if (steps % 10000 == 0) { | ||
154 | System.out.println("Steps: " + steps + " / " + MAX); | ||
105 | } | 155 | } |
106 | } | 156 | } |
157 | |||
158 | static final double limit = 0.01; | ||
159 | |||
160 | @Test | ||
161 | void test0() { | ||
162 | assertEquals(1, generate(0, true).versions); | ||
163 | } | ||
164 | |||
165 | @ParameterizedTest | ||
166 | @ValueSource(ints = {1, 2, 3, 4}) | ||
167 | void testForSmallUntypedModels(int size) { | ||
168 | var res = generate(size, false); | ||
169 | System.out.println(res); | ||
170 | assertTrue(res.failureRatio() < limit); | ||
171 | } | ||
172 | |||
173 | @ParameterizedTest | ||
174 | @ValueSource(ints = {1, 2, 3}) | ||
175 | void testForSmallTypedModels(int size) { | ||
176 | var res = generate(size, true); | ||
177 | System.out.println(res); | ||
178 | assertTrue(res.failureRatio() < limit); | ||
179 | } | ||
180 | |||
181 | @Test | ||
182 | @Tag("fuzz") | ||
183 | @Tag("slow") | ||
184 | void testForLargeTypedModels() { | ||
185 | var res = generate(10, true); | ||
186 | System.out.println(res); | ||
187 | assertTrue(res.failureRatio() < limit); | ||
188 | } | ||
189 | |||
190 | @Test | ||
191 | @Tag("fuzz") | ||
192 | @Tag("slow") | ||
193 | void testForLargeUntypedModels() { | ||
194 | var res = generate(10, false); | ||
195 | System.out.println(res); | ||
196 | assertTrue(res.failureRatio() < limit); | ||
197 | } | ||
107 | } | 198 | } |
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..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 | |||
@@ -19,11 +19,10 @@ class StateCoderBuildTest { | |||
19 | Symbol<Boolean> friend = new Symbol<>("friend", 2, Boolean.class, false); | 19 | Symbol<Boolean> friend = new Symbol<>("friend", 2, Boolean.class, false); |
20 | 20 | ||
21 | @Test | 21 | @Test |
22 | void simpleStateCoderTest() { | 22 | void simpleStateCoderBuildTest() { |
23 | var store = ModelStore.builder() | 23 | var store = ModelStore.builder() |
24 | .symbols(person, age, friend) | 24 | .symbols(person, age, friend) |
25 | .with(StateCoderAdapter | 25 | .with(StateCoderAdapter.builder()) |
26 | .builder()) | ||
27 | .build(); | 26 | .build(); |
28 | 27 | ||
29 | var model = store.createEmptyModel(); | 28 | var model = store.createEmptyModel(); |
@@ -33,6 +32,7 @@ class StateCoderBuildTest { | |||
33 | var personI = model.getInterpretation(person); | 32 | var personI = model.getInterpretation(person); |
34 | var friendI = model.getInterpretation(friend); | 33 | var friendI = model.getInterpretation(friend); |
35 | var ageI = model.getInterpretation(age); | 34 | var ageI = model.getInterpretation(age); |
35 | |||
36 | fill(personI, friendI, ageI); | 36 | fill(personI, friendI, ageI); |
37 | 37 | ||
38 | stateCoder.calculateStateCode(); | 38 | stateCoder.calculateStateCode(); |
@@ -58,16 +58,107 @@ class StateCoderBuildTest { | |||
58 | 58 | ||
59 | int code = stateCoder.calculateStateCode().modelCode(); | 59 | int code = stateCoder.calculateStateCode().modelCode(); |
60 | 60 | ||
61 | ageI.put(Tuple.of(1),3); | 61 | ageI.put(Tuple.of(1), 3); |
62 | assertEquals(code,stateCoder.calculateStateCode().modelCode()); | 62 | assertEquals(code, stateCoder.calculateStateCode().modelCode()); |
63 | |||
64 | ageI.put(Tuple.of(1), null); | ||
65 | assertEquals(code, stateCoder.calculateStateCode().modelCode()); | ||
66 | |||
67 | personI.put(Tuple.of(2), false); | ||
68 | assertEquals(code, stateCoder.calculateStateCode().modelCode()); | ||
69 | } | ||
70 | |||
71 | @Test | ||
72 | void notIndividualTest() { | ||
73 | var store = ModelStore.builder() | ||
74 | .symbols(friend) | ||
75 | .with(StateCoderAdapter.builder()) | ||
76 | .build(); | ||
77 | |||
78 | var model = store.createEmptyModel(); | ||
79 | var stateCoder = model.getAdapter(StateCoderAdapter.class); | ||
80 | |||
81 | var friendI = model.getInterpretation(friend); | ||
82 | |||
83 | friendI.put(Tuple.of(1, 2), true); | ||
84 | int code1 = stateCoder.calculateModelCode(); | ||
63 | 85 | ||
64 | ageI.put(Tuple.of(1),null); | 86 | friendI.put(Tuple.of(1, 2), false); |
65 | assertEquals(code,stateCoder.calculateStateCode().modelCode()); | 87 | friendI.put(Tuple.of(2, 1), true); |
88 | int code2 = stateCoder.calculateModelCode(); | ||
66 | 89 | ||
67 | personI.put(Tuple.of(2),false); | 90 | assertEquals(code1, code2); |
68 | assertEquals(code,stateCoder.calculateStateCode().modelCode()); | ||
69 | } | 91 | } |
70 | 92 | ||
93 | @Test | ||
94 | void individualTest() { | ||
95 | var store = ModelStore.builder() | ||
96 | .symbols(friend) | ||
97 | .with(StateCoderAdapter.builder() | ||
98 | .individual(Tuple.of(1))) | ||
99 | .build(); | ||
100 | |||
101 | var model = store.createEmptyModel(); | ||
102 | var stateCoder = model.getAdapter(StateCoderAdapter.class); | ||
103 | |||
104 | var friendI = model.getInterpretation(friend); | ||
105 | |||
106 | friendI.put(Tuple.of(1, 2), true); | ||
107 | int code1 = stateCoder.calculateModelCode(); | ||
108 | |||
109 | friendI.put(Tuple.of(1, 2), false); | ||
110 | friendI.put(Tuple.of(2, 1), true); | ||
111 | int code2 = stateCoder.calculateModelCode(); | ||
112 | |||
113 | assertNotEquals(code1, code2); | ||
114 | } | ||
115 | |||
116 | @Test | ||
117 | void customStateCoderTest() { | ||
118 | final boolean[] called = new boolean[]{false}; | ||
119 | StateCodeCalculator mock = () -> { | ||
120 | called[0] = true; | ||
121 | return null; | ||
122 | }; | ||
123 | |||
124 | var store = ModelStore.builder() | ||
125 | .symbols(friend) | ||
126 | .with(StateCoderAdapter.builder() | ||
127 | .stateCodeCalculatorFactory((interpretations, individuals) -> mock)) | ||
128 | .build(); | ||
129 | |||
130 | var model = store.createEmptyModel(); | ||
131 | var stateCoder = model.getAdapter(StateCoderAdapter.class); | ||
132 | |||
133 | stateCoder.calculateStateCode(); | ||
134 | |||
135 | assertTrue(called[0]); | ||
136 | } | ||
137 | |||
138 | @Test | ||
139 | void customEquivalenceCheckerTest() { | ||
140 | final boolean[] called = new boolean[]{false}; | ||
141 | StateEquivalenceChecker mock = (p1, p2, p3, p4, p5) -> { | ||
142 | called[0] = true; | ||
143 | return StateEquivalenceChecker.EquivalenceResult.UNKNOWN; | ||
144 | }; | ||
145 | |||
146 | var store = ModelStore.builder() | ||
147 | .symbols(friend) | ||
148 | .with(StateCoderAdapter.builder() | ||
149 | .stateEquivalenceChecker(mock)) | ||
150 | .build(); | ||
151 | |||
152 | var model = store.createEmptyModel(); | ||
153 | var v1 = model.commit(); | ||
154 | var v2 = model.commit(); | ||
155 | |||
156 | store.getAdapter(StateCoderStoreAdapter.class).checkEquivalence(v1, v2); | ||
157 | |||
158 | assertTrue(called[0]); | ||
159 | } | ||
160 | |||
161 | |||
71 | private static void fill(Interpretation<Boolean> personI, Interpretation<Boolean> friendI, Interpretation<Integer> ageI) { | 162 | private static void fill(Interpretation<Boolean> personI, Interpretation<Boolean> friendI, Interpretation<Integer> ageI) { |
72 | personI.put(Tuple.of(1), true); | 163 | personI.put(Tuple.of(1), true); |
73 | personI.put(Tuple.of(2), true); | 164 | personI.put(Tuple.of(2), true); |
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 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.statecoding; | ||
7 | |||
8 | import org.junit.jupiter.api.Test; | ||
9 | import tools.refinery.store.model.Model; | ||
10 | import tools.refinery.store.model.ModelStore; | ||
11 | import tools.refinery.store.representation.Symbol; | ||
12 | import tools.refinery.store.tuple.Tuple; | ||
13 | |||
14 | import java.util.Objects; | ||
15 | |||
16 | import static org.junit.jupiter.api.Assertions.*; | ||
17 | |||
18 | class StateCoderUnitTest { | ||
19 | Symbol<Boolean> person = new Symbol<>("Person", 1, Boolean.class, false); | ||
20 | Symbol<Integer> age = new Symbol<>("age", 1, Integer.class, null); | ||
21 | Symbol<Boolean> friend = new Symbol<>("friend", 2, Boolean.class, false); | ||
22 | Symbol<Boolean> parents = new Symbol<>("parents", 3, Boolean.class, false); | ||
23 | Symbol<Integer> population = new Symbol<>("population", 0, Integer.class, 0); | ||
24 | |||
25 | private Model createEmptyModel() { | ||
26 | var store = ModelStore.builder() | ||
27 | .symbols(person, age, friend, parents, population) | ||
28 | .with(StateCoderAdapter.builder()) | ||
29 | .build(); | ||
30 | |||
31 | return store.createEmptyModel(); | ||
32 | } | ||
33 | |||
34 | @Test | ||
35 | void emptyModelCode0() { | ||
36 | Model model = createEmptyModel(); | ||
37 | var stateCoder = model.getAdapter(StateCoderAdapter.class); | ||
38 | |||
39 | assertEquals(0, stateCoder.calculateModelCode()); | ||
40 | |||
41 | var personI = model.getInterpretation(person); | ||
42 | var friendI = model.getInterpretation(friend); | ||
43 | |||
44 | personI.put(Tuple.of(1), true); | ||
45 | personI.put(Tuple.of(2), true); | ||
46 | friendI.put(Tuple.of(1, 2), true); | ||
47 | |||
48 | assertNotEquals(0, stateCoder.calculateModelCode()); | ||
49 | } | ||
50 | |||
51 | @Test | ||
52 | void emptyObjectCode0() { | ||
53 | Model model = createEmptyModel(); | ||
54 | var stateCoder = model.getAdapter(StateCoderAdapter.class); | ||
55 | |||
56 | var personI = model.getInterpretation(person); | ||
57 | var friendI = model.getInterpretation(friend); | ||
58 | |||
59 | assertEquals(0, stateCoder.calculateObjectCode().get(1)); | ||
60 | assertEquals(0, stateCoder.calculateObjectCode().get(17)); | ||
61 | |||
62 | personI.put(Tuple.of(1), true); | ||
63 | personI.put(Tuple.of(2), true); | ||
64 | friendI.put(Tuple.of(1, 2), true); | ||
65 | |||
66 | assertNotEquals(0, stateCoder.calculateObjectCode().get(1)); | ||
67 | assertEquals(0, stateCoder.calculateObjectCode().get(17)); | ||
68 | } | ||
69 | |||
70 | @Test | ||
71 | void nullRelationTest() { | ||
72 | Model model = createEmptyModel(); | ||
73 | var stateCoder = model.getAdapter(StateCoderAdapter.class); | ||
74 | |||
75 | var populationI = model.getInterpretation(population); | ||
76 | |||
77 | final int hashOf0 = Objects.hashCode(0); | ||
78 | |||
79 | assertEquals(hashOf0, stateCoder.calculateModelCode()); | ||
80 | |||
81 | populationI.put(Tuple.of(), 1); | ||
82 | int code1 = stateCoder.calculateModelCode(); | ||
83 | |||
84 | assertNotEquals(hashOf0, stateCoder.calculateModelCode()); | ||
85 | |||
86 | populationI.put(Tuple.of(), 2); | ||
87 | int code2 = stateCoder.calculateModelCode(); | ||
88 | |||
89 | assertNotEquals(code1, code2); | ||
90 | |||
91 | populationI.put(Tuple.of(), 1); | ||
92 | assertEquals(code1, stateCoder.calculateModelCode()); | ||
93 | } | ||
94 | |||
95 | @Test | ||
96 | void unaryBooleanTest() { | ||
97 | Model model = createEmptyModel(); | ||
98 | var stateCoder = model.getAdapter(StateCoderAdapter.class); | ||
99 | |||
100 | var personI = model.getInterpretation(person); | ||
101 | |||
102 | assertEquals(0, stateCoder.calculateModelCode()); | ||
103 | |||
104 | personI.put(Tuple.of(1), true); | ||
105 | int code1 = stateCoder.calculateModelCode(); | ||
106 | |||
107 | assertNotEquals(0, stateCoder.calculateModelCode()); | ||
108 | |||
109 | personI.put(Tuple.of(2), true); | ||
110 | int code2 = stateCoder.calculateModelCode(); | ||
111 | |||
112 | assertNotEquals(code1, code2); | ||
113 | |||
114 | personI.put(Tuple.of(1), false); | ||
115 | assertEquals(code1, stateCoder.calculateModelCode()); | ||
116 | } | ||
117 | |||
118 | @Test | ||
119 | void unaryIntTest() { | ||
120 | Model model = createEmptyModel(); | ||
121 | var stateCoder = model.getAdapter(StateCoderAdapter.class); | ||
122 | |||
123 | var ageI = model.getInterpretation(age); | ||
124 | |||
125 | assertEquals(0, stateCoder.calculateModelCode()); | ||
126 | |||
127 | ageI.put(Tuple.of(1), 4); | ||
128 | int code0 = stateCoder.calculateModelCode(); | ||
129 | |||
130 | assertNotEquals(0, code0); | ||
131 | |||
132 | ageI.put(Tuple.of(1), 5); | ||
133 | int code1 = stateCoder.calculateModelCode(); | ||
134 | |||
135 | assertNotEquals(code0, code1); | ||
136 | |||
137 | ageI.put(Tuple.of(2), 5); | ||
138 | int code2 = stateCoder.calculateModelCode(); | ||
139 | |||
140 | assertNotEquals(code1, code2); | ||
141 | |||
142 | ageI.put(Tuple.of(1), null); | ||
143 | assertEquals(code1, stateCoder.calculateModelCode()); | ||
144 | } | ||
145 | |||
146 | @Test | ||
147 | void binaryTest() { | ||
148 | Model model = createEmptyModel(); | ||
149 | var stateCoder = model.getAdapter(StateCoderAdapter.class); | ||
150 | |||
151 | var friendI = model.getInterpretation(friend); | ||
152 | |||
153 | assertEquals(0, stateCoder.calculateModelCode()); | ||
154 | |||
155 | friendI.put(Tuple.of(1, 2), true); | ||
156 | int code1 = stateCoder.calculateModelCode(); | ||
157 | |||
158 | assertNotEquals(0, code1); | ||
159 | |||
160 | friendI.put(Tuple.of(2, 1), true); | ||
161 | int code2 = stateCoder.calculateModelCode(); | ||
162 | |||
163 | assertNotEquals(code1, code2); | ||
164 | |||
165 | friendI.put(Tuple.of(1, 2), false); | ||
166 | int code3 = stateCoder.calculateModelCode(); | ||
167 | |||
168 | assertEquals(code1, code3); | ||
169 | } | ||
170 | |||
171 | @Test | ||
172 | void NaryTest() { | ||
173 | Model model = createEmptyModel(); | ||
174 | var stateCoder = model.getAdapter(StateCoderAdapter.class); | ||
175 | |||
176 | var parentsI = model.getInterpretation(parents); | ||
177 | |||
178 | assertEquals(0, stateCoder.calculateModelCode()); | ||
179 | |||
180 | parentsI.put(Tuple.of(3, 1, 2), true); | ||
181 | int code1 = stateCoder.calculateModelCode(); | ||
182 | |||
183 | assertNotEquals(0, code1); | ||
184 | |||
185 | parentsI.put(Tuple.of(4, 1, 2), true); | ||
186 | int code2 = stateCoder.calculateModelCode(); | ||
187 | |||
188 | assertNotEquals(code1, code2); | ||
189 | |||
190 | parentsI.put(Tuple.of(3, 1, 2), false); | ||
191 | int code3 = stateCoder.calculateModelCode(); | ||
192 | |||
193 | assertEquals(code1, code3); | ||
194 | } | ||
195 | } | ||