diff options
Diffstat (limited to 'subprojects/store/src/main')
9 files changed, 137 insertions, 23 deletions
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/DNF.java b/subprojects/store/src/main/java/tools/refinery/store/query/DNF.java index 336c25a1..3ce4eed9 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/query/DNF.java +++ b/subprojects/store/src/main/java/tools/refinery/store/query/DNF.java | |||
@@ -5,22 +5,48 @@ import tools.refinery.store.query.atom.DNFAtom; | |||
5 | 5 | ||
6 | import java.util.*; | 6 | import java.util.*; |
7 | 7 | ||
8 | public class DNF implements SymbolLike { | 8 | public final class DNF implements SymbolLike { |
9 | private final String name; | 9 | private final String name; |
10 | 10 | ||
11 | private final String uniqueName; | 11 | private final String uniqueName; |
12 | 12 | ||
13 | private final List<Variable> parameters; | 13 | private final List<Variable> parameters; |
14 | 14 | ||
15 | private final List<FunctionalDependency<Variable>> functionalDependencies; | ||
16 | |||
15 | private final List<DNFAnd> clauses; | 17 | private final List<DNFAnd> clauses; |
16 | 18 | ||
17 | private DNF(String name, List<Variable> parameters, List<DNFAnd> clauses) { | 19 | private DNF(String name, List<Variable> parameters, List<FunctionalDependency<Variable>> functionalDependencies, |
20 | List<DNFAnd> clauses) { | ||
21 | validateFunctionalDependencies(parameters, functionalDependencies); | ||
18 | this.name = name; | 22 | this.name = name; |
19 | this.uniqueName = DNFUtils.generateUniqueName(name); | 23 | this.uniqueName = DNFUtils.generateUniqueName(name); |
20 | this.parameters = parameters; | 24 | this.parameters = parameters; |
25 | this.functionalDependencies = functionalDependencies; | ||
21 | this.clauses = clauses; | 26 | this.clauses = clauses; |
22 | } | 27 | } |
23 | 28 | ||
29 | private static void validateFunctionalDependencies( | ||
30 | Collection<Variable> parameters, Collection<FunctionalDependency<Variable>> functionalDependencies) { | ||
31 | var parameterSet = new HashSet<>(parameters); | ||
32 | for (var functionalDependency : functionalDependencies) { | ||
33 | validateParameters(parameters, parameterSet, functionalDependency.forEach(), functionalDependency); | ||
34 | validateParameters(parameters, parameterSet, functionalDependency.unique(), functionalDependency); | ||
35 | } | ||
36 | } | ||
37 | |||
38 | private static void validateParameters(Collection<Variable> parameters, Set<Variable> parameterSet, | ||
39 | Collection<Variable> toValidate, | ||
40 | FunctionalDependency<Variable> functionalDependency) { | ||
41 | for (var variable : toValidate) { | ||
42 | if (!parameterSet.contains(variable)) { | ||
43 | throw new IllegalArgumentException( | ||
44 | "Variable %s of functional dependency %s does not appear in the parameter list %s" | ||
45 | .formatted(variable, functionalDependency, parameters)); | ||
46 | } | ||
47 | } | ||
48 | } | ||
49 | |||
24 | @Override | 50 | @Override |
25 | public String name() { | 51 | public String name() { |
26 | return name; | 52 | return name; |
@@ -34,6 +60,10 @@ public class DNF implements SymbolLike { | |||
34 | return parameters; | 60 | return parameters; |
35 | } | 61 | } |
36 | 62 | ||
63 | public List<FunctionalDependency<Variable>> getFunctionalDependencies() { | ||
64 | return functionalDependencies; | ||
65 | } | ||
66 | |||
37 | @Override | 67 | @Override |
38 | public int arity() { | 68 | public int arity() { |
39 | return parameters.size(); | 69 | return parameters.size(); |
@@ -57,6 +87,8 @@ public class DNF implements SymbolLike { | |||
57 | 87 | ||
58 | private final List<Variable> parameters = new ArrayList<>(); | 88 | private final List<Variable> parameters = new ArrayList<>(); |
59 | 89 | ||
90 | private final List<FunctionalDependency<Variable>> functionalDependencies = new ArrayList<>(); | ||
91 | |||
60 | private final List<List<DNFAtom>> clauses = new ArrayList<>(); | 92 | private final List<List<DNFAtom>> clauses = new ArrayList<>(); |
61 | 93 | ||
62 | private Builder(String name) { | 94 | private Builder(String name) { |
@@ -64,7 +96,7 @@ public class DNF implements SymbolLike { | |||
64 | } | 96 | } |
65 | 97 | ||
66 | public Builder parameter(Variable variable) { | 98 | public Builder parameter(Variable variable) { |
67 | this.parameters.add(variable); | 99 | parameters.add(variable); |
68 | return this; | 100 | return this; |
69 | } | 101 | } |
70 | 102 | ||
@@ -73,10 +105,24 @@ public class DNF implements SymbolLike { | |||
73 | } | 105 | } |
74 | 106 | ||
75 | public Builder parameters(Collection<Variable> variables) { | 107 | public Builder parameters(Collection<Variable> variables) { |
76 | this.parameters.addAll(variables); | 108 | parameters.addAll(variables); |
77 | return this; | 109 | return this; |
78 | } | 110 | } |
79 | 111 | ||
112 | public Builder functionalDependencies(Collection<FunctionalDependency<Variable>> functionalDependencies) { | ||
113 | this.functionalDependencies.addAll(functionalDependencies); | ||
114 | return this; | ||
115 | } | ||
116 | |||
117 | public Builder functionalDependency(FunctionalDependency<Variable> functionalDependency) { | ||
118 | functionalDependencies.add(functionalDependency); | ||
119 | return this; | ||
120 | } | ||
121 | |||
122 | public Builder functionalDependency(Set<Variable> forEach, Set<Variable> unique) { | ||
123 | return functionalDependency(new FunctionalDependency<>(forEach, unique)); | ||
124 | } | ||
125 | |||
80 | public Builder clause(DNFAtom... atoms) { | 126 | public Builder clause(DNFAtom... atoms) { |
81 | clauses.add(List.of(atoms)); | 127 | clauses.add(List.of(atoms)); |
82 | return this; | 128 | return this; |
@@ -113,9 +159,12 @@ public class DNF implements SymbolLike { | |||
113 | constraint.collectAllVariables(variables); | 159 | constraint.collectAllVariables(variables); |
114 | } | 160 | } |
115 | parameters.forEach(variables::remove); | 161 | parameters.forEach(variables::remove); |
116 | postProcessedClauses.add(new DNFAnd(variables, constraints)); | 162 | postProcessedClauses.add(new DNFAnd(Collections.unmodifiableSet(variables), |
163 | Collections.unmodifiableList(constraints))); | ||
117 | } | 164 | } |
118 | return new DNF(name, List.copyOf(parameters), postProcessedClauses); | 165 | return new DNF(name, Collections.unmodifiableList(parameters), |
166 | Collections.unmodifiableList(functionalDependencies), | ||
167 | Collections.unmodifiableList(postProcessedClauses)); | ||
119 | } | 168 | } |
120 | } | 169 | } |
121 | } | 170 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/FunctionalDependency.java b/subprojects/store/src/main/java/tools/refinery/store/query/FunctionalDependency.java new file mode 100644 index 00000000..63a81713 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/query/FunctionalDependency.java | |||
@@ -0,0 +1,15 @@ | |||
1 | package tools.refinery.store.query; | ||
2 | |||
3 | import java.util.HashSet; | ||
4 | import java.util.Set; | ||
5 | |||
6 | public record FunctionalDependency<T>(Set<T> forEach, Set<T> unique) { | ||
7 | public FunctionalDependency { | ||
8 | var uniqueForEach = new HashSet<>(unique); | ||
9 | uniqueForEach.retainAll(forEach); | ||
10 | if (!uniqueForEach.isEmpty()) { | ||
11 | throw new IllegalArgumentException("Variables %s appear on both sides of the functional dependency" | ||
12 | .formatted(uniqueForEach)); | ||
13 | } | ||
14 | } | ||
15 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java b/subprojects/store/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java index 853a1796..4364d844 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java +++ b/subprojects/store/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java | |||
@@ -11,7 +11,7 @@ public interface ModelQueryBuilder extends ModelAdapterBuilder { | |||
11 | return queries(List.of(queries)); | 11 | return queries(List.of(queries)); |
12 | } | 12 | } |
13 | 13 | ||
14 | default ModelQueryBuilder queries(Collection<? extends DNF> queries) { | 14 | default ModelQueryBuilder queries(Collection<DNF> queries) { |
15 | queries.forEach(this::query); | 15 | queries.forEach(this::query); |
16 | return this; | 16 | return this; |
17 | } | 17 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/AnyRelationView.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/AnyRelationView.java index 9df1038b..eb64b589 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/query/view/AnyRelationView.java +++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/AnyRelationView.java | |||
@@ -1,12 +1,23 @@ | |||
1 | package tools.refinery.store.query.view; | 1 | package tools.refinery.store.query.view; |
2 | 2 | ||
3 | import tools.refinery.store.model.Model; | 3 | import tools.refinery.store.model.Model; |
4 | import tools.refinery.store.representation.SymbolLike; | 4 | import tools.refinery.store.query.FunctionalDependency; |
5 | import tools.refinery.store.representation.AnySymbol; | 5 | import tools.refinery.store.representation.AnySymbol; |
6 | import tools.refinery.store.representation.SymbolLike; | ||
7 | |||
8 | import java.util.Set; | ||
6 | 9 | ||
7 | public sealed interface AnyRelationView extends SymbolLike permits RelationView { | 10 | public sealed interface AnyRelationView extends SymbolLike permits RelationView { |
8 | AnySymbol getSymbol(); | 11 | AnySymbol getSymbol(); |
9 | 12 | ||
13 | default Set<FunctionalDependency<Integer>> getFunctionalDependencies() { | ||
14 | return Set.of(); | ||
15 | } | ||
16 | |||
17 | default Set<RelationViewImplication> getImpliedRelationViews() { | ||
18 | return Set.of(); | ||
19 | } | ||
20 | |||
10 | boolean get(Model model, Object[] tuple); | 21 | boolean get(Model model, Object[] tuple); |
11 | 22 | ||
12 | Iterable<Object[]> getAll(Model model); | 23 | Iterable<Object[]> getAll(Model model); |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java index d263679a..3d278a8b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java +++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java | |||
@@ -1,11 +1,16 @@ | |||
1 | package tools.refinery.store.query.view; | 1 | package tools.refinery.store.query.view; |
2 | 2 | ||
3 | import tools.refinery.store.model.Model; | 3 | import tools.refinery.store.model.Model; |
4 | import tools.refinery.store.query.FunctionalDependency; | ||
5 | import tools.refinery.store.representation.Symbol; | ||
4 | import tools.refinery.store.tuple.Tuple; | 6 | import tools.refinery.store.tuple.Tuple; |
5 | import tools.refinery.store.tuple.Tuple1; | 7 | import tools.refinery.store.tuple.Tuple1; |
6 | import tools.refinery.store.representation.Symbol; | ||
7 | 8 | ||
8 | public class FunctionalRelationView<T> extends RelationView<T> { | 9 | import java.util.Set; |
10 | import java.util.stream.Collectors; | ||
11 | import java.util.stream.IntStream; | ||
12 | |||
13 | public final class FunctionalRelationView<T> extends RelationView<T> { | ||
9 | public FunctionalRelationView(Symbol<T> symbol, String name) { | 14 | public FunctionalRelationView(Symbol<T> symbol, String name) { |
10 | super(symbol, name); | 15 | super(symbol, name); |
11 | } | 16 | } |
@@ -15,13 +20,35 @@ public class FunctionalRelationView<T> extends RelationView<T> { | |||
15 | } | 20 | } |
16 | 21 | ||
17 | @Override | 22 | @Override |
23 | public Set<FunctionalDependency<Integer>> getFunctionalDependencies() { | ||
24 | var arity = getSymbol().arity(); | ||
25 | var forEach = IntStream.range(0, arity).boxed().collect(Collectors.toUnmodifiableSet()); | ||
26 | var unique = Set.of(arity); | ||
27 | return Set.of(new FunctionalDependency<>(forEach, unique)); | ||
28 | } | ||
29 | |||
30 | @Override | ||
31 | public Set<RelationViewImplication> getImpliedRelationViews() { | ||
32 | var symbol = getSymbol(); | ||
33 | var impliedIndices = IntStream.range(0, symbol.arity()).boxed().toList(); | ||
34 | var keyOnlyRelationView = new KeyOnlyRelationView<>(symbol); | ||
35 | return Set.of(new RelationViewImplication(this, keyOnlyRelationView, impliedIndices)); | ||
36 | } | ||
37 | |||
38 | @Override | ||
18 | public boolean filter(Tuple key, T value) { | 39 | public boolean filter(Tuple key, T value) { |
19 | return true; | 40 | return true; |
20 | } | 41 | } |
21 | 42 | ||
22 | @Override | 43 | @Override |
23 | public Object[] forwardMap(Tuple key, T value) { | 44 | public Object[] forwardMap(Tuple key, T value) { |
24 | return toTuple1ArrayPlusValue(key, value); | 45 | int size = key.getSize(); |
46 | Object[] result = new Object[size + 1]; | ||
47 | for (int i = 0; i < size; i++) { | ||
48 | result[i] = Tuple.of(key.get(i)); | ||
49 | } | ||
50 | result[key.getSize()] = value; | ||
51 | return result; | ||
25 | } | 52 | } |
26 | 53 | ||
27 | @Override | 54 | @Override |
@@ -41,13 +68,4 @@ public class FunctionalRelationView<T> extends RelationView<T> { | |||
41 | public int arity() { | 68 | public int arity() { |
42 | return getSymbol().arity() + 1; | 69 | return getSymbol().arity() + 1; |
43 | } | 70 | } |
44 | |||
45 | private static <D> Object[] toTuple1ArrayPlusValue(Tuple t, D value) { | ||
46 | Object[] result = new Object[t.getSize() + 1]; | ||
47 | for (int i = 0; i < t.getSize(); i++) { | ||
48 | result[i] = Tuple.of(t.get(i)); | ||
49 | } | ||
50 | result[t.getSize()] = value; | ||
51 | return result; | ||
52 | } | ||
53 | } | 71 | } |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java index fdfcdf28..e1b2e45b 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java +++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java | |||
@@ -5,7 +5,7 @@ import tools.refinery.store.tuple.Tuple; | |||
5 | 5 | ||
6 | import java.util.Objects; | 6 | import java.util.Objects; |
7 | 7 | ||
8 | public class KeyOnlyRelationView<T> extends TuplePreservingRelationView<T> { | 8 | public final class KeyOnlyRelationView<T> extends TuplePreservingRelationView<T> { |
9 | public static final String VIEW_NAME = "key"; | 9 | public static final String VIEW_NAME = "key"; |
10 | 10 | ||
11 | private final T defaultValue; | 11 | private final T defaultValue; |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/query/view/RelationViewImplication.java b/subprojects/store/src/main/java/tools/refinery/store/query/view/RelationViewImplication.java new file mode 100644 index 00000000..2ba1fcc4 --- /dev/null +++ b/subprojects/store/src/main/java/tools/refinery/store/query/view/RelationViewImplication.java | |||
@@ -0,0 +1,19 @@ | |||
1 | package tools.refinery.store.query.view; | ||
2 | |||
3 | import java.util.List; | ||
4 | |||
5 | public record RelationViewImplication(AnyRelationView implyingRelationView, AnyRelationView impliedRelationView, | ||
6 | List<Integer> impliedIndices) { | ||
7 | public RelationViewImplication { | ||
8 | if (impliedIndices.size() != impliedRelationView.arity()) { | ||
9 | throw new IllegalArgumentException("Expected %d implied indices for %s, but %d are provided" | ||
10 | .formatted(impliedRelationView.arity(), impliedRelationView, impliedIndices.size())); | ||
11 | } | ||
12 | for (var index : impliedIndices) { | ||
13 | if (impliedRelationView.invalidIndex(index)) { | ||
14 | throw new IllegalArgumentException("%d is not a valid index for %s".formatted(index, | ||
15 | implyingRelationView)); | ||
16 | } | ||
17 | } | ||
18 | } | ||
19 | } | ||
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/Symbol.java b/subprojects/store/src/main/java/tools/refinery/store/representation/Symbol.java index 404b5ddb..77fae5f4 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/Symbol.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/Symbol.java | |||
@@ -2,8 +2,6 @@ package tools.refinery.store.representation; | |||
2 | 2 | ||
3 | import java.util.Objects; | 3 | import java.util.Objects; |
4 | 4 | ||
5 | // Deliberately not a record, because we want equality by reference. | ||
6 | @SuppressWarnings({"squid:S6206", "ClassCanBeRecord"}) | ||
7 | public final class Symbol<T> implements AnySymbol { | 5 | public final class Symbol<T> implements AnySymbol { |
8 | private final String name; | 6 | private final String name; |
9 | private final int arity; | 7 | private final int arity; |
diff --git a/subprojects/store/src/main/java/tools/refinery/store/representation/SymbolLike.java b/subprojects/store/src/main/java/tools/refinery/store/representation/SymbolLike.java index 8c84500c..30a892ae 100644 --- a/subprojects/store/src/main/java/tools/refinery/store/representation/SymbolLike.java +++ b/subprojects/store/src/main/java/tools/refinery/store/representation/SymbolLike.java | |||
@@ -4,4 +4,8 @@ public interface SymbolLike { | |||
4 | String name(); | 4 | String name(); |
5 | 5 | ||
6 | int arity(); | 6 | int arity(); |
7 | |||
8 | default boolean invalidIndex(int i) { | ||
9 | return i < 0 || i >= arity(); | ||
10 | } | ||
7 | } | 11 | } |