diff options
Diffstat (limited to 'subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed')
5 files changed, 293 insertions, 27 deletions
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/MapBasedSeed.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/MapBasedSeed.java new file mode 100644 index 00000000..8b1c3bb3 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/MapBasedSeed.java | |||
@@ -0,0 +1,111 @@ | |||
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.reasoning.seed; | ||
7 | |||
8 | import tools.refinery.store.map.Cursor; | ||
9 | import tools.refinery.store.map.Cursors; | ||
10 | import tools.refinery.store.tuple.Tuple; | ||
11 | |||
12 | import java.util.Map; | ||
13 | import java.util.Objects; | ||
14 | |||
15 | record MapBasedSeed<T>(int arity, Class<T> valueType, T reducedValue, Map<Tuple, T> map) implements Seed<T> { | ||
16 | @Override | ||
17 | public T get(Tuple key) { | ||
18 | var value = map.get(key); | ||
19 | return value == null ? reducedValue : value; | ||
20 | } | ||
21 | |||
22 | @Override | ||
23 | public Cursor<Tuple, T> getCursor(T defaultValue, int nodeCount) { | ||
24 | if (Objects.equals(defaultValue, reducedValue)) { | ||
25 | return Cursors.of(map); | ||
26 | } | ||
27 | return new CartesianProductCursor<>(arity, nodeCount, reducedValue, defaultValue, map); | ||
28 | } | ||
29 | |||
30 | private static class CartesianProductCursor<T> implements Cursor<Tuple, T> { | ||
31 | private final int nodeCount; | ||
32 | private final T reducedValue; | ||
33 | private final T defaultValue; | ||
34 | private final Map<Tuple, T> map; | ||
35 | private final int[] counter; | ||
36 | private State state = State.INITIAL; | ||
37 | private Tuple key; | ||
38 | private T value; | ||
39 | |||
40 | private CartesianProductCursor(int arity, int nodeCount, T reducedValue, T defaultValue, Map<Tuple, T> map) { | ||
41 | this.nodeCount = nodeCount; | ||
42 | this.reducedValue = reducedValue; | ||
43 | this.defaultValue = defaultValue; | ||
44 | this.map = map; | ||
45 | counter = new int[arity]; | ||
46 | } | ||
47 | |||
48 | @Override | ||
49 | public Tuple getKey() { | ||
50 | return key; | ||
51 | } | ||
52 | |||
53 | @Override | ||
54 | public T getValue() { | ||
55 | return value; | ||
56 | } | ||
57 | |||
58 | @Override | ||
59 | public boolean isTerminated() { | ||
60 | return state == State.TERMINATED; | ||
61 | } | ||
62 | |||
63 | @Override | ||
64 | public boolean move() { | ||
65 | return switch (state) { | ||
66 | case INITIAL -> { | ||
67 | state = State.STARTED; | ||
68 | yield checkValue() || moveToNext(); | ||
69 | } | ||
70 | case STARTED -> moveToNext(); | ||
71 | case TERMINATED -> false; | ||
72 | }; | ||
73 | } | ||
74 | |||
75 | private boolean moveToNext() { | ||
76 | do { | ||
77 | increment(); | ||
78 | } while (state != State.TERMINATED && !checkValue()); | ||
79 | return state != State.TERMINATED; | ||
80 | } | ||
81 | |||
82 | private void increment() { | ||
83 | int i = counter.length - 1; | ||
84 | while (i >= 0) { | ||
85 | counter[i]++; | ||
86 | if (counter[i] < nodeCount) { | ||
87 | return; | ||
88 | } | ||
89 | counter[i] = 0; | ||
90 | i--; | ||
91 | } | ||
92 | state = State.TERMINATED; | ||
93 | } | ||
94 | |||
95 | private boolean checkValue() { | ||
96 | key = Tuple.of(counter); | ||
97 | var valueInMap = map.get(key); | ||
98 | if (Objects.equals(valueInMap, defaultValue)) { | ||
99 | return false; | ||
100 | } | ||
101 | value = valueInMap == null ? reducedValue : valueInMap; | ||
102 | return true; | ||
103 | } | ||
104 | |||
105 | private enum State { | ||
106 | INITIAL, | ||
107 | STARTED, | ||
108 | TERMINATED | ||
109 | } | ||
110 | } | ||
111 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java new file mode 100644 index 00000000..e6b3eaf9 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/ModelSeed.java | |||
@@ -0,0 +1,95 @@ | |||
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.reasoning.seed; | ||
7 | |||
8 | import tools.refinery.store.map.Cursor; | ||
9 | import tools.refinery.store.reasoning.representation.AnyPartialSymbol; | ||
10 | import tools.refinery.store.reasoning.representation.PartialSymbol; | ||
11 | import tools.refinery.store.tuple.Tuple; | ||
12 | |||
13 | import java.util.Collections; | ||
14 | import java.util.LinkedHashMap; | ||
15 | import java.util.Map; | ||
16 | import java.util.Set; | ||
17 | import java.util.function.Consumer; | ||
18 | |||
19 | public class ModelSeed { | ||
20 | private final int nodeCount; | ||
21 | private final Map<AnyPartialSymbol, Seed<?>> seeds; | ||
22 | |||
23 | private ModelSeed(int nodeCount, Map<AnyPartialSymbol, Seed<?>> seeds) { | ||
24 | this.nodeCount = nodeCount; | ||
25 | this.seeds = seeds; | ||
26 | } | ||
27 | |||
28 | public int getNodeCount() { | ||
29 | return nodeCount; | ||
30 | } | ||
31 | |||
32 | public <A> Seed<A> getSeed(PartialSymbol<A, ?> partialSymbol) { | ||
33 | var seed = seeds.get(partialSymbol); | ||
34 | if (seed == null) { | ||
35 | throw new IllegalArgumentException("No seed for partial symbol " + partialSymbol); | ||
36 | } | ||
37 | // The builder makes sure only well-typed seeds can be added. | ||
38 | @SuppressWarnings("unchecked") | ||
39 | var typedSeed = (Seed<A>) seed; | ||
40 | return typedSeed; | ||
41 | } | ||
42 | |||
43 | public boolean containsSeed(AnyPartialSymbol symbol) { | ||
44 | return seeds.containsKey(symbol); | ||
45 | } | ||
46 | |||
47 | public Set<AnyPartialSymbol> getSeededSymbols() { | ||
48 | return Collections.unmodifiableSet(seeds.keySet()); | ||
49 | } | ||
50 | |||
51 | public <A> Cursor<Tuple, A> getCursor(PartialSymbol<A, ?> partialSymbol, A defaultValue) { | ||
52 | return getSeed(partialSymbol).getCursor(defaultValue, nodeCount); | ||
53 | } | ||
54 | |||
55 | public static Builder builder(int nodeCount) { | ||
56 | return new Builder(nodeCount); | ||
57 | } | ||
58 | |||
59 | public static class Builder { | ||
60 | private final int nodeCount; | ||
61 | private final Map<AnyPartialSymbol, Seed<?>> seeds = new LinkedHashMap<>(); | ||
62 | |||
63 | private Builder(int nodeCount) { | ||
64 | if (nodeCount < 0) { | ||
65 | throw new IllegalArgumentException("Node count must not be negative"); | ||
66 | } | ||
67 | this.nodeCount = nodeCount; | ||
68 | } | ||
69 | |||
70 | public <A> Builder seed(PartialSymbol<A, ?> partialSymbol, Seed<A> seed) { | ||
71 | if (seed.arity() != partialSymbol.arity()) { | ||
72 | throw new IllegalStateException("Expected seed of arity %d for partial symbol %s, but got %d instead" | ||
73 | .formatted(partialSymbol.arity(), partialSymbol, seed.arity())); | ||
74 | } | ||
75 | if (!seed.valueType().equals(partialSymbol.abstractDomain().abstractType())) { | ||
76 | throw new IllegalStateException("Expected seed of type %s for partial symbol %s, but got %s instead" | ||
77 | .formatted(partialSymbol.abstractDomain().abstractType(), partialSymbol, seed.valueType())); | ||
78 | } | ||
79 | if (seeds.put(partialSymbol, seed) != null) { | ||
80 | throw new IllegalArgumentException("Duplicate seed for partial symbol " + partialSymbol); | ||
81 | } | ||
82 | return this; | ||
83 | } | ||
84 | |||
85 | public <A> Builder seed(PartialSymbol<A, ?> partialSymbol, Consumer<Seed.Builder<A>> callback) { | ||
86 | var builder = Seed.builder(partialSymbol); | ||
87 | callback.accept(builder); | ||
88 | return seed(partialSymbol, builder.build()); | ||
89 | } | ||
90 | |||
91 | public ModelSeed build() { | ||
92 | return new ModelSeed(nodeCount, Collections.unmodifiableMap(seeds)); | ||
93 | } | ||
94 | } | ||
95 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java index 08079f12..732efbcc 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java | |||
@@ -6,14 +6,73 @@ | |||
6 | package tools.refinery.store.reasoning.seed; | 6 | package tools.refinery.store.reasoning.seed; |
7 | 7 | ||
8 | import tools.refinery.store.map.Cursor; | 8 | import tools.refinery.store.map.Cursor; |
9 | import tools.refinery.store.reasoning.representation.PartialSymbol; | ||
10 | import tools.refinery.store.representation.Symbol; | ||
9 | import tools.refinery.store.tuple.Tuple; | 11 | import tools.refinery.store.tuple.Tuple; |
10 | 12 | ||
13 | import java.util.Collections; | ||
14 | import java.util.LinkedHashMap; | ||
15 | import java.util.Map; | ||
16 | |||
11 | public interface Seed<T> { | 17 | public interface Seed<T> { |
12 | int arity(); | 18 | int arity(); |
13 | 19 | ||
20 | Class<T> valueType(); | ||
21 | |||
14 | T reducedValue(); | 22 | T reducedValue(); |
15 | 23 | ||
16 | T get(Tuple key); | 24 | T get(Tuple key); |
17 | 25 | ||
18 | Cursor<Tuple, T> getCursor(T defaultValue, int nodeCount); | 26 | Cursor<Tuple, T> getCursor(T defaultValue, int nodeCount); |
27 | |||
28 | static <T> Builder<T> builder(int arity, Class<T> valueType, T reducedValue) { | ||
29 | return new Builder<>(arity, valueType, reducedValue); | ||
30 | } | ||
31 | |||
32 | static <T> Builder<T> builder(Symbol<T> symbol) { | ||
33 | return builder(symbol.arity(), symbol.valueType(), symbol.defaultValue()); | ||
34 | } | ||
35 | |||
36 | static <T> Builder<T> builder(PartialSymbol<T, ?> partialSymbol) { | ||
37 | return builder(partialSymbol.arity(), partialSymbol.abstractDomain().abstractType(), | ||
38 | partialSymbol.defaultValue()); | ||
39 | } | ||
40 | |||
41 | @SuppressWarnings("UnusedReturnValue") | ||
42 | class Builder<T> { | ||
43 | private final int arity; | ||
44 | private final Class<T> valueType; | ||
45 | private T reducedValue; | ||
46 | private final Map<Tuple, T> map = new LinkedHashMap<>(); | ||
47 | |||
48 | private Builder(int arity, Class<T> valueType, T reducedValue) { | ||
49 | this.arity = arity; | ||
50 | this.valueType = valueType; | ||
51 | this.reducedValue = reducedValue; | ||
52 | } | ||
53 | |||
54 | public Builder<T> reducedValue(T reducedValue) { | ||
55 | this.reducedValue = reducedValue; | ||
56 | return this; | ||
57 | } | ||
58 | |||
59 | public Builder<T> put(Tuple key, T value) { | ||
60 | if (key.getSize() != arity) { | ||
61 | throw new IllegalArgumentException("Expected %s to have %d elements".formatted(key, arity)); | ||
62 | } | ||
63 | map.put(key, value); | ||
64 | return this; | ||
65 | } | ||
66 | |||
67 | public Builder<T> putAll(Map<Tuple, T> map) { | ||
68 | for (var entry : map.entrySet()) { | ||
69 | put(entry.getKey(), entry.getValue()); | ||
70 | } | ||
71 | return this; | ||
72 | } | ||
73 | |||
74 | public Seed<T> build() { | ||
75 | return new MapBasedSeed<>(arity, valueType, reducedValue, Collections.unmodifiableMap(map)); | ||
76 | } | ||
77 | } | ||
19 | } | 78 | } |
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/SeedInitializer.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/SeedInitializer.java new file mode 100644 index 00000000..9af457d8 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/SeedInitializer.java | |||
@@ -0,0 +1,28 @@ | |||
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.reasoning.seed; | ||
7 | |||
8 | import tools.refinery.store.model.Model; | ||
9 | import tools.refinery.store.reasoning.refinement.PartialModelInitializer; | ||
10 | import tools.refinery.store.reasoning.representation.PartialSymbol; | ||
11 | import tools.refinery.store.representation.Symbol; | ||
12 | |||
13 | public class SeedInitializer<T> implements PartialModelInitializer { | ||
14 | private final Symbol<T> symbol; | ||
15 | private final PartialSymbol<T, ?> partialSymbol; | ||
16 | |||
17 | public SeedInitializer(Symbol<T> symbol, PartialSymbol<T, ?> partialSymbol) { | ||
18 | this.symbol = symbol; | ||
19 | this.partialSymbol = partialSymbol; | ||
20 | } | ||
21 | |||
22 | @Override | ||
23 | public void initialize(Model model, ModelSeed modelSeed) { | ||
24 | var interpretation = model.getInterpretation(symbol); | ||
25 | var cursor = modelSeed.getCursor(partialSymbol, symbol.defaultValue()); | ||
26 | interpretation.putAll(cursor); | ||
27 | } | ||
28 | } | ||
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/UniformSeed.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/UniformSeed.java deleted file mode 100644 index 451d1513..00000000 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/UniformSeed.java +++ /dev/null | |||
@@ -1,27 +0,0 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.store.reasoning.seed; | ||
7 | |||
8 | import tools.refinery.store.map.Cursor; | ||
9 | import tools.refinery.store.tuple.Tuple; | ||
10 | |||
11 | public record UniformSeed<T>(int arity, T reducedValue) implements Seed<T> { | ||
12 | public UniformSeed { | ||
13 | if (arity < 0) { | ||
14 | throw new IllegalArgumentException("Arity must not be negative"); | ||
15 | } | ||
16 | } | ||
17 | |||
18 | @Override | ||
19 | public T get(Tuple key) { | ||
20 | return reducedValue; | ||
21 | } | ||
22 | |||
23 | @Override | ||
24 | public Cursor<Tuple, T> getCursor(T defaultValue, int nodeCount) { | ||
25 | return null; | ||
26 | } | ||
27 | } | ||