aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/store/src
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-02-02 16:33:45 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-02-02 16:33:45 +0100
commita69beacc266ba4462377cecb010a437cf6a12428 (patch)
tree01de8ad0ea53e6fbc9e446c7344340e96fd6eebd /subprojects/store/src
parentrefactor: VIATRA adapter fixes (diff)
downloadrefinery-a69beacc266ba4462377cecb010a437cf6a12428.tar.gz
refinery-a69beacc266ba4462377cecb010a437cf6a12428.tar.zst
refinery-a69beacc266ba4462377cecb010a437cf6a12428.zip
feat: model query functional dependencies
Diffstat (limited to 'subprojects/store/src')
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/DNF.java61
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/FunctionalDependency.java15
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/view/AnyRelationView.java13
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java42
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/view/KeyOnlyRelationView.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/query/view/RelationViewImplication.java19
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/Symbol.java2
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/representation/SymbolLike.java4
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
6import java.util.*; 6import java.util.*;
7 7
8public class DNF implements SymbolLike { 8public 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 @@
1package tools.refinery.store.query;
2
3import java.util.HashSet;
4import java.util.Set;
5
6public 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 @@
1package tools.refinery.store.query.view; 1package tools.refinery.store.query.view;
2 2
3import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.representation.SymbolLike; 4import tools.refinery.store.query.FunctionalDependency;
5import tools.refinery.store.representation.AnySymbol; 5import tools.refinery.store.representation.AnySymbol;
6import tools.refinery.store.representation.SymbolLike;
7
8import java.util.Set;
6 9
7public sealed interface AnyRelationView extends SymbolLike permits RelationView { 10public 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 @@
1package tools.refinery.store.query.view; 1package tools.refinery.store.query.view;
2 2
3import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.FunctionalDependency;
5import tools.refinery.store.representation.Symbol;
4import tools.refinery.store.tuple.Tuple; 6import tools.refinery.store.tuple.Tuple;
5import tools.refinery.store.tuple.Tuple1; 7import tools.refinery.store.tuple.Tuple1;
6import tools.refinery.store.representation.Symbol;
7 8
8public class FunctionalRelationView<T> extends RelationView<T> { 9import java.util.Set;
10import java.util.stream.Collectors;
11import java.util.stream.IntStream;
12
13public 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
6import java.util.Objects; 6import java.util.Objects;
7 7
8public class KeyOnlyRelationView<T> extends TuplePreservingRelationView<T> { 8public 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 @@
1package tools.refinery.store.query.view;
2
3import java.util.List;
4
5public 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
3import java.util.Objects; 3import java.util.Objects;
4 4
5// Deliberately not a record, because we want equality by reference.
6@SuppressWarnings({"squid:S6206", "ClassCanBeRecord"})
7public final class Symbol<T> implements AnySymbol { 5public 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}