From f4d45c97b5d36974001c89884c04213cce924dd0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Tue, 30 Jul 2024 21:29:25 +0200 Subject: [PATCH 01/10] refactor(reasoning): propagation in refiner Make sure the propagations from the model seed are applied in the same way as during value merging. --- .../crossreference/typeConstraint.problem | 85 +++++++++++++++++++ .../internal/ReasoningAdapterImpl.java | 10 +++ .../internal/ReasoningStoreAdapterImpl.java | 3 + .../AnyPartialInterpretationRefiner.java | 33 +++++++ .../refinement/ConcreteSymbolRefiner.java | 12 ++- .../store/reasoning/seed/ModelSeed.java | 4 + .../store/reasoning/seed/SeedInitializer.java | 8 ++ .../containment/ContainmentLinkRefiner.java | 9 +- .../containment/ContainsRefiner.java | 9 +- .../DirectedCrossReferenceInitializer.java | 52 ------------ .../DirectedCrossReferenceRefiner.java | 36 ++++++-- .../DirectedCrossReferenceTranslator.java | 3 +- .../UndirectedCrossReferenceInitializer.java | 18 +--- .../UndirectedCrossReferenceRefiner.java | 43 ++++++++-- .../UndirectedCrossReferenceTranslator.java | 4 +- 15 files changed, 239 insertions(+), 90 deletions(-) create mode 100644 subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/typeConstraint.problem delete mode 100644 subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceInitializer.java diff --git a/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/typeConstraint.problem b/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/typeConstraint.problem new file mode 100644 index 000000000..4c24fa5ae --- /dev/null +++ b/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/typeConstraint.problem @@ -0,0 +1,85 @@ +% Copyright (c) 2024 The Refinery Authors +% +% SPDX-License-Identifier: EPL-2.0 + +% TEST WITH ERRORS: directed cross reference type constraint + +class A { + B[] foo +} + +class B. + +foo(a1, b1). +!foo(a2, b2). +?foo(a3, b3). +foo(a4, b4): error. + +% EXPECT EXACTLY: +foo(a1, b1). +!foo(a2, b2). +?foo(a3, b3). +foo(a4, b4): error. +A(a1). +B(b1). +?A(a2). +?B(b2). +?A(a3). +?B(b3). +A(a4). +B(b4). + +% TEST: directed cross reference with predicate type + +class A { + bar[] foo +} + +pred bar(A a) <-> !foo(a, _). + +foo(a1, b1). + +% EXPECT: +A(a1). +bar(b1). + +% TEST WITH ERRORS: directed cross reference with predicate type and assertion + +class A { + bar[] foo +} + +pred bar(A a) <-> !foo(a, _). + +!bar(b1). +foo(a1, b1). + +% EXPECT EXACTLY: +foo(a1, b1): error. +A(a1). +bar(b1): error. + +% TEST WITH ERRORS: undirected cross reference type constraint + +class A { + A[] bar opposite bar +} + +bar(a1, b1). +!bar(a2, b2). +?bar(a3, b3). +bar(a4, b4): error. + +% EXPECT EXACTLY: +bar(a1, b1). +!bar(a2, b2). +?bar(a3, b3). +bar(a4, b4): error. +A(a1). +A(b1). +?A(a2). +?A(b2). +?A(a3). +?A(b3). +A(a4). +A(b4). diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java index b83d162e0..8994ac6eb 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java @@ -18,6 +18,7 @@ import tools.refinery.store.reasoning.refinement.StorageRefiner; import tools.refinery.store.reasoning.representation.AnyPartialSymbol; import tools.refinery.store.reasoning.representation.PartialSymbol; +import tools.refinery.store.reasoning.seed.ModelSeed; import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; import tools.refinery.store.representation.Symbol; import tools.refinery.logic.term.cardinalityinterval.CardinalityInterval; @@ -106,6 +107,9 @@ private void createRefiners() { var refiner = createRefiner(factory, partialSymbol); refiners.put(partialSymbol, refiner); } + for (var refiner : refiners.values()) { + refiner.afterCreate(); + } } private , C> PartialInterpretationRefiner createRefiner( @@ -214,4 +218,10 @@ public int getNodeCount() { Integer nodeCount = nodeCountInterpretation.get(Tuple.of()); return nodeCount == null ? 0 : nodeCount; } + + void afterInitialize(ModelSeed modelSeed) { + for (var refiner : refiners.values()) { + refiner.afterInitialize(modelSeed); + } + } } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java index df4b64571..57cbb5d0c 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java @@ -10,6 +10,7 @@ import tools.refinery.store.model.Model; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryAdapter; +import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.ReasoningStoreAdapter; import tools.refinery.store.reasoning.interpretation.PartialInterpretation; import tools.refinery.store.reasoning.literal.Concreteness; @@ -107,6 +108,8 @@ public PropagatedModel tryCreateInitialModel(ModelSeed modelSeed) { for (var initializer : initializers) { initializer.initialize(model, modelSeed); } + var reasoningAdapter = ((ReasoningAdapterImpl) model.getAdapter(ReasoningAdapter.class)); + reasoningAdapter.afterInitialize(modelSeed); var propagationResult = model.tryGetAdapter(PropagationAdapter.class) .map(PropagationAdapter::propagate) .orElse(PropagationResult.UNCHANGED); diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/AnyPartialInterpretationRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/AnyPartialInterpretationRefiner.java index 6c3815113..7fd17a3ae 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/AnyPartialInterpretationRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/AnyPartialInterpretationRefiner.java @@ -5,11 +5,44 @@ */ package tools.refinery.store.reasoning.refinement; +import tools.refinery.logic.AbstractValue; +import tools.refinery.store.model.Model; import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.representation.AnyPartialSymbol; +import tools.refinery.store.reasoning.seed.ModelSeed; +import tools.refinery.store.tuple.Tuple; public sealed interface AnyPartialInterpretationRefiner permits PartialInterpretationRefiner { ReasoningAdapter getAdapter(); AnyPartialSymbol getPartialSymbol(); + + /** + * Called after all {@link PartialInterpretationRefiner} instances have been created for symbols registered to + * the {@link tools.refinery.store.reasoning.ReasoningStoreAdapter}. + *

+ * Override this method to access other {@link PartialInterpretationRefiner} instances associated to the + * {@link ReasoningAdapter} that are required for propagations executed by this + * {@link PartialInterpretationRefiner}. + */ + default void afterCreate() { + } + + /** + * Execute propagations based on the contents of the {@code modelSeed} that would by executed if the + * {@code modelSeed} were written to the model as a sequence of + * {@link PartialInterpretationRefiner#merge(Tuple, AbstractValue)} calls. + *

+ * This method is called only after {@link PartialModelInitializer#initialize(Model, ModelSeed)} was called on all + * {@link PartialModelInitializer} instances registered to the + * {@link tools.refinery.store.reasoning.ReasoningStoreAdapter}. + *

+ * The default implementation of this method performs no actions. Override it make the behavior consistent with + * your {@link PartialInterpretationRefiner#merge(Tuple, AbstractValue)} implementation. + * + * @param modelSeed The model seed which was written by a previous call of + * {@link PartialModelInitializer#initialize(Model, ModelSeed)}. + */ + default void afterInitialize(ModelSeed modelSeed) { + } } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/ConcreteSymbolRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/ConcreteSymbolRefiner.java index d6ac0e9db..5474a4ff0 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/ConcreteSymbolRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/refinement/ConcreteSymbolRefiner.java @@ -26,14 +26,22 @@ public ConcreteSymbolRefiner(ReasoningAdapter adapter, PartialSymbol parti @Override public boolean merge(Tuple key, A value) { - var currentValue = interpretation.get(key); + var currentValue = get(key); var mergedValue = currentValue.meet(value); if (!Objects.equals(currentValue, mergedValue)) { - interpretation.put(key, mergedValue); + put(key, mergedValue); } return true; } + protected A get(Tuple key) { + return interpretation.get(key); + } + + protected A put(Tuple key, A value) { + return interpretation.put(key, value); + } + public static , C1> Factory of(Symbol concreteSymbol) { return (adapter, partialSymbol) -> new ConcreteSymbolRefiner<>(adapter, partialSymbol, concreteSymbol); } 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 index 9cd4862b2..31f6fa046 100644 --- 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 @@ -54,6 +54,10 @@ public Set getSeededSymbols() { return getSeed(partialSymbol).getCursor(defaultValue, nodeCount); } + public > Cursor getCursor(PartialSymbol partialSymbol) { + return getCursor(partialSymbol, partialSymbol.defaultValue()); + } + public static Builder builder(int nodeCount) { return new Builder(nodeCount); } 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 index 138e3a640..adc6421fc 100644 --- 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 @@ -20,6 +20,14 @@ public SeedInitializer(Symbol symbol, PartialSymbol partialSymbol) { this.partialSymbol = partialSymbol; } + protected Symbol getSymbol() { + return symbol; + } + + protected PartialSymbol getPartialSymbol() { + return partialSymbol; + } + @Override public void initialize(Model model, ModelSeed modelSeed) { var interpretation = model.getInterpretation(symbol); diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentLinkRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentLinkRefiner.java index adbea26b1..14331dfc1 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentLinkRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainmentLinkRefiner.java @@ -21,14 +21,19 @@ class ContainmentLinkRefiner extends AbstractPartialInterpretationRefiner { private final Factory factory; private final Interpretation interpretation; - private final PartialInterpretationRefiner sourceRefiner; - private final PartialInterpretationRefiner targetRefiner; + private PartialInterpretationRefiner sourceRefiner; + private PartialInterpretationRefiner targetRefiner; private ContainmentLinkRefiner(ReasoningAdapter adapter, PartialSymbol partialSymbol, Factory factory) { super(adapter, partialSymbol); this.factory = factory; interpretation = adapter.getModel().getInterpretation(factory.symbol); + } + + @Override + public void afterCreate() { + var adapter = getAdapter(); sourceRefiner = adapter.getRefiner(factory.sourceType); targetRefiner = adapter.getRefiner(factory.targetType); } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainsRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainsRefiner.java index 024774bc3..4e5029bf6 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainsRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/containment/ContainsRefiner.java @@ -30,13 +30,18 @@ class ContainsRefiner extends AbstractPartialInterpretationRefiner interpretation; - private final PartialInterpretationRefiner containerRefiner; - private final PartialInterpretationRefiner containedRefiner; + private PartialInterpretationRefiner containerRefiner; + private PartialInterpretationRefiner containedRefiner; private ContainsRefiner(ReasoningAdapter adapter, PartialSymbol partialSymbol, Symbol containsStorage) { super(adapter, partialSymbol); interpretation = adapter.getModel().getInterpretation(containsStorage); + } + + @Override + public void afterCreate() { + var adapter = getAdapter(); containerRefiner = adapter.getRefiner(ContainmentHierarchyTranslator.CONTAINER_SYMBOL); containedRefiner = adapter.getRefiner(ContainmentHierarchyTranslator.CONTAINED_SYMBOL); } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceInitializer.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceInitializer.java deleted file mode 100644 index 7cb16a282..000000000 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceInitializer.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.store.reasoning.translator.crossreference; - -import tools.refinery.store.model.Model; -import tools.refinery.store.reasoning.ReasoningAdapter; -import tools.refinery.store.reasoning.refinement.PartialModelInitializer; -import tools.refinery.store.reasoning.representation.PartialRelation; -import tools.refinery.store.reasoning.seed.ModelSeed; -import tools.refinery.store.representation.Symbol; -import tools.refinery.logic.term.truthvalue.TruthValue; -import tools.refinery.store.tuple.Tuple; - -class DirectedCrossReferenceInitializer implements PartialModelInitializer { - private final PartialRelation linkType; - private final PartialRelation sourceType; - private final PartialRelation targetType; - private final Symbol symbol; - - public DirectedCrossReferenceInitializer(PartialRelation linkType, PartialRelation sourceType, - PartialRelation targetType, Symbol symbol) { - this.linkType = linkType; - this.sourceType = sourceType; - this.targetType = targetType; - this.symbol = symbol; - } - - @Override - public void initialize(Model model, ModelSeed modelSeed) { - var reasoningAdapter = model.getAdapter(ReasoningAdapter.class); - var sourceRefiner = reasoningAdapter.getRefiner(sourceType); - var targetRefiner = reasoningAdapter.getRefiner(targetType); - var interpretation = model.getInterpretation(symbol); - var cursor = modelSeed.getCursor(linkType, symbol.defaultValue()); - while (cursor.move()) { - var key = cursor.getKey(); - var value = cursor.getValue(); - interpretation.put(key, value); - if (value.must()) { - boolean merged = sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && - targetRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); - if (!merged) { - throw new IllegalArgumentException("Failed to merge end types of reference %s for key %s" - .formatted(linkType, key)); - } - } - } - } -} diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java index 75dd5dad3..7a9599915 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java @@ -5,27 +5,35 @@ */ package tools.refinery.store.reasoning.translator.crossreference; +import tools.refinery.logic.term.truthvalue.TruthValue; import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner; import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.reasoning.representation.PartialSymbol; +import tools.refinery.store.reasoning.seed.ModelSeed; import tools.refinery.store.representation.Symbol; -import tools.refinery.logic.term.truthvalue.TruthValue; import tools.refinery.store.tuple.Tuple; class DirectedCrossReferenceRefiner extends ConcreteSymbolRefiner { + private final PartialRelation sourceType; private final PartialRelation targetType; - private final PartialInterpretationRefiner sourceRefiner; + private PartialInterpretationRefiner sourceRefiner; private PartialInterpretationRefiner targetRefiner; public DirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol partialSymbol, Symbol concreteSymbol, PartialRelation sourceType, PartialRelation targetType) { super(adapter, partialSymbol, concreteSymbol); + this.sourceType = sourceType; this.targetType = targetType; - // Source is always a class, so we can rely on the fact that it is always constructed before this refiner. + } + + @Override + public void afterCreate() { + var adapter = getAdapter(); sourceRefiner = adapter.getRefiner(sourceType); + targetRefiner = adapter.getRefiner(targetType); } @Override @@ -34,16 +42,30 @@ public boolean merge(Tuple key, TruthValue value) { return false; } if (value.must()) { - if (targetRefiner == null) { - // Access the target refinery lazily, since it may be constructed after this refiner. - targetRefiner = getAdapter().getRefiner(targetType); - } return sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && targetRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); } return true; } + @Override + public void afterInitialize(ModelSeed modelSeed) { + var linkType = getPartialSymbol(); + var cursor = modelSeed.getCursor(linkType); + while (cursor.move()) { + var value = cursor.getValue(); + if (value.must()) { + var key = cursor.getKey(); + boolean merged = sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && + targetRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); + if (!merged) { + throw new IllegalArgumentException("Failed to merge end types of reference %s for key %s" + .formatted(linkType, key)); + } + } + } + } + public static Factory of(Symbol concreteSymbol, PartialRelation sourceType, PartialRelation targetType) { return (adapter, partialSymbol) -> new DirectedCrossReferenceRefiner(adapter, partialSymbol, concreteSymbol, diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java index d4c2afd25..c98971d96 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceTranslator.java @@ -47,7 +47,7 @@ public void apply(ModelStoreBuilder storeBuilder) { var targetType = info.targetType(); var defaultValue = info.defaultValue(); if (defaultValue.must()) { - throw new TranslationException(linkType, "Unsupported default value %s for directed cross references %s" + throw new TranslationException(linkType, "Unsupported default value %s for directed cross reference %s" .formatted(defaultValue, linkType)); } var translator = PartialRelationTranslator.of(linkType); @@ -58,7 +58,6 @@ public void apply(ModelStoreBuilder storeBuilder) { configureWithDefaultFalse(storeBuilder); } translator.refiner(DirectedCrossReferenceRefiner.of(symbol, sourceType, targetType)); - translator.initializer(new DirectedCrossReferenceInitializer(linkType, sourceType, targetType, symbol)); if (info.partial()) { translator.roundingMode(RoundingMode.NONE); } else { diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceInitializer.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceInitializer.java index 84dcfdc56..d0784c272 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceInitializer.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceInitializer.java @@ -6,48 +6,34 @@ package tools.refinery.store.reasoning.translator.crossreference; import org.jetbrains.annotations.NotNull; +import tools.refinery.logic.term.truthvalue.TruthValue; import tools.refinery.store.model.Model; -import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.refinement.PartialModelInitializer; import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.reasoning.seed.ModelSeed; import tools.refinery.store.reasoning.translator.TranslationException; import tools.refinery.store.representation.Symbol; -import tools.refinery.logic.term.truthvalue.TruthValue; import tools.refinery.store.tuple.Tuple; import java.util.LinkedHashMap; class UndirectedCrossReferenceInitializer implements PartialModelInitializer { private final PartialRelation linkType; - private final PartialRelation sourceType; private final Symbol symbol; - UndirectedCrossReferenceInitializer(PartialRelation linkType, PartialRelation sourceType, - Symbol symbol) { + UndirectedCrossReferenceInitializer(PartialRelation linkType, Symbol symbol) { this.linkType = linkType; - this.sourceType = sourceType; this.symbol = symbol; } @Override public void initialize(Model model, ModelSeed modelSeed) { - var reasoningAdapter = model.getAdapter(ReasoningAdapter.class); var mergedMap = getMergedMap(modelSeed); - var sourceRefiner = reasoningAdapter.getRefiner(sourceType); var interpretation = model.getInterpretation(symbol); for (var entry : mergedMap.entrySet()) { var key = entry.getKey(); var value = entry.getValue(); interpretation.put(key, value); - if (value.must()) { - boolean merged = sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && - sourceRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); - if (!merged) { - throw new IllegalArgumentException("Failed to merge end types of reference %s for key %s" - .formatted(linkType, key)); - } - } } } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java index 54aca80f6..79251d13d 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java @@ -5,30 +5,45 @@ */ package tools.refinery.store.reasoning.translator.crossreference; +import tools.refinery.logic.term.truthvalue.TruthValue; import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner; import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.reasoning.representation.PartialSymbol; +import tools.refinery.store.reasoning.seed.ModelSeed; import tools.refinery.store.representation.Symbol; -import tools.refinery.logic.term.truthvalue.TruthValue; import tools.refinery.store.tuple.Tuple; +import java.util.Objects; + class UndirectedCrossReferenceRefiner extends ConcreteSymbolRefiner { - private final PartialInterpretationRefiner sourceRefiner; + private final PartialRelation sourceType; + private PartialInterpretationRefiner sourceRefiner; public UndirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol partialSymbol, Symbol concreteSymbol, PartialRelation sourceType) { super(adapter, partialSymbol, concreteSymbol); - sourceRefiner = adapter.getRefiner(sourceType); + this.sourceType = sourceType; + } + + @Override + public void afterCreate() { + sourceRefiner = getAdapter().getRefiner(sourceType); } @Override public boolean merge(Tuple key, TruthValue value) { int source = key.get(0); int target = key.get(1); - if (!super.merge(key, value) || !super.merge(Tuple.of(target, source), value)) { - return false; + var currentValue = get(key); + var mergedValue = currentValue.meet(value); + if (!Objects.equals(currentValue, mergedValue)) { + var oldValue = put(key, mergedValue); + var inverseOldValue = put(Tuple.of(target, source), mergedValue); + if (!Objects.equals(oldValue, inverseOldValue)) { + return false; + } } if (value.must()) { return sourceRefiner.merge(Tuple.of(source), TruthValue.TRUE) && @@ -37,6 +52,24 @@ public boolean merge(Tuple key, TruthValue value) { return true; } + @Override + public void afterInitialize(ModelSeed modelSeed) { + var linkType = getPartialSymbol(); + var cursor = modelSeed.getCursor(linkType); + while (cursor.move()) { + var value = cursor.getValue(); + if (value.must()) { + var key = cursor.getKey(); + boolean merged = sourceRefiner.merge(Tuple.of(key.get(0)), TruthValue.TRUE) && + sourceRefiner.merge(Tuple.of(key.get(1)), TruthValue.TRUE); + if (!merged) { + throw new IllegalArgumentException("Failed to merge end types of reference %s for key %s" + .formatted(linkType, key)); + } + } + } + } + public static Factory of(Symbol concreteSymbol, PartialRelation sourceType) { return (adapter, partialSymbol) -> new UndirectedCrossReferenceRefiner(adapter, partialSymbol, concreteSymbol, sourceType); diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java index 494211fed..0de1deac2 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceTranslator.java @@ -44,7 +44,7 @@ public void apply(ModelStoreBuilder storeBuilder) { var type = info.type(); var defaultValue = info.defaultValue(); if (defaultValue.must()) { - throw new TranslationException(linkType, "Unsupported default value %s for directed cross references %s" + throw new TranslationException(linkType, "Unsupported default value %s for undirected cross reference %s" .formatted(defaultValue, linkType)); } var translator = PartialRelationTranslator.of(linkType); @@ -54,8 +54,8 @@ public void apply(ModelStoreBuilder storeBuilder) { } else { configureWithDefaultFalse(storeBuilder); } + translator.initializer(new UndirectedCrossReferenceInitializer(linkType, symbol)); translator.refiner(UndirectedCrossReferenceRefiner.of(symbol, type)); - translator.initializer(new UndirectedCrossReferenceInitializer(linkType, type, symbol)); if (info.partial()) { translator.roundingMode(RoundingMode.NONE); } else { From 228a54b5b4cf21a4dbf5e45fd50361cec3068f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Thu, 1 Aug 2024 18:27:10 +0200 Subject: [PATCH 02/10] feat: base predicates --- subprojects/frontend/src/graph/GraphStore.ts | 1 + .../frontend/src/language/problem.grammar | 3 +- .../frontend/src/xtext/xtextServiceResults.ts | 1 + subprojects/language-model/problem.aird | 88 +++++++++-- .../src/main/resources/model/problem.ecore | 9 +- .../src/main/resources/model/problem.genmodel | 9 +- .../language/semantics/ModelInitializer.java | 42 +++++- .../metadata/BasePredicateDetail.java | 10 ++ .../semantics/metadata/MetadataCreator.java | 5 +- .../semantics/metadata/RelationDetail.java | 2 +- .../web/xtext/servlet/XtextWebSocket.java | 3 +- .../tools/refinery/language/Problem.xtext | 11 +- .../state/ProblemDerivedStateComputer.java | 4 +- .../language/typesystem/TypedModule.java | 2 +- .../refinery/language/utils/ProblemUtil.java | 18 ++- .../language/validation/ProblemValidator.java | 9 -- .../DirectedCrossReferenceRefiner.java | 6 +- .../UndirectedCrossReferenceRefiner.java | 5 +- .../predicate/BasePredicateTranslator.java | 141 ++++++++++++++++++ .../predicate/PredicateRefiner.java | 94 ++++++++++++ .../predicate/PredicateTranslator.java | 13 +- 21 files changed, 421 insertions(+), 55 deletions(-) create mode 100644 subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/BasePredicateDetail.java create mode 100644 subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java create mode 100644 subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java diff --git a/subprojects/frontend/src/graph/GraphStore.ts b/subprojects/frontend/src/graph/GraphStore.ts index 30d0a2f33..a133d636a 100644 --- a/subprojects/frontend/src/graph/GraphStore.ts +++ b/subprojects/frontend/src/graph/GraphStore.ts @@ -25,6 +25,7 @@ export function getDefaultVisibility( case 'class': case 'reference': case 'opposite': + case 'base': return 'all'; case 'predicate': return detail.error ? 'must' : 'none'; diff --git a/subprojects/frontend/src/language/problem.grammar b/subprojects/frontend/src/language/problem.grammar index b8038b70c..56867964b 100644 --- a/subprojects/frontend/src/language/problem.grammar +++ b/subprojects/frontend/src/language/problem.grammar @@ -52,8 +52,7 @@ statement { kw<"extern"> ckw<"aggregator"> AggregatorName "." } | PredicateDefinition { - ckw<"shadow">? - (kw<"error">? kw<"pred"> | kw<"error">) + ((kw<"error"> | kw<"partial"> | ckw<"shadow">)* kw<"pred"> | kw<"error">) RelationName ParameterList? PredicateBody { ("<->" sep)? "." } } | diff --git a/subprojects/frontend/src/xtext/xtextServiceResults.ts b/subprojects/frontend/src/xtext/xtextServiceResults.ts index c5bc13205..7c2fb8ec4 100644 --- a/subprojects/frontend/src/xtext/xtextServiceResults.ts +++ b/subprojects/frontend/src/xtext/xtextServiceResults.ts @@ -156,6 +156,7 @@ export const RelationMetadata = z.object({ opposite: z.string(), }), z.object({ type: z.literal('predicate'), error: z.boolean() }), + z.object({ type: z.literal('base') }), z.object({ type: z.literal('builtin') }), ]), }); diff --git a/subprojects/language-model/problem.aird b/subprojects/language-model/problem.aird index 7c1f7368e..f2495bcf5 100644 --- a/subprojects/language-model/problem.aird +++ b/subprojects/language-model/problem.aird @@ -7,7 +7,7 @@ build/resources/main/model/problem.genmodel - + @@ -91,10 +91,6 @@ - - - - @@ -494,6 +490,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + @@ -1591,22 +1612,14 @@ - - - + + + - - - - - - - - @@ -2865,6 +2878,49 @@ + + + + KEEP_LOCATION + KEEP_SIZE + KEEP_RATIO + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/subprojects/language-model/src/main/resources/model/problem.ecore b/subprojects/language-model/src/main/resources/model/problem.ecore index a9b6f660b..0bc8caf9d 100644 --- a/subprojects/language-model/src/main/resources/model/problem.ecore +++ b/subprojects/language-model/src/main/resources/model/problem.ecore @@ -34,8 +34,7 @@ - - + @@ -281,4 +280,10 @@ + + + + + + diff --git a/subprojects/language-model/src/main/resources/model/problem.genmodel b/subprojects/language-model/src/main/resources/model/problem.genmodel index f3c367e1a..e7cf6843d 100644 --- a/subprojects/language-model/src/main/resources/model/problem.genmodel +++ b/subprojects/language-model/src/main/resources/model/problem.genmodel @@ -81,6 +81,12 @@ + + + + + + @@ -105,8 +111,7 @@ - - + diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java index 6aea31327..3defc0c0f 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java @@ -39,6 +39,7 @@ import tools.refinery.store.reasoning.translator.multiplicity.ConstrainedMultiplicity; import tools.refinery.store.reasoning.translator.multiplicity.Multiplicity; import tools.refinery.store.reasoning.translator.multiplicity.UnconstrainedMultiplicity; +import tools.refinery.store.reasoning.translator.predicate.BasePredicateTranslator; import tools.refinery.store.reasoning.translator.predicate.PredicateTranslator; import tools.refinery.store.statecoding.StateCoderBuilder; import tools.refinery.store.tuple.Tuple; @@ -291,7 +292,7 @@ private void collectClassDeclarationSymbols(ClassDeclaration classDeclaration) { private void collectPredicateDefinitionSymbol(PredicateDefinition predicateDefinition) { int arity = predicateDefinition.getParameters().size(); - if (predicateDefinition.isError()) { + if (predicateDefinition.getKind() == PredicateKind.ERROR) { collectPartialRelation(predicateDefinition, arity, TruthValue.FALSE, TruthValue.FALSE); } else { collectPartialRelation(predicateDefinition, arity, null, TruthValue.UNKNOWN); @@ -596,16 +597,26 @@ private void collectPredicateDefinitionTraced(PredicateDefinition predicateDefin } private void collectPredicateDefinition(PredicateDefinition predicateDefinition, ModelStoreBuilder storeBuilder) { + if (ProblemUtil.isBasePredicate(predicateDefinition)) { + collectBasePredicateDefinition(predicateDefinition, storeBuilder); + } else { + collectComputedPredicateDefinition(predicateDefinition, storeBuilder); + } + } + + private void collectComputedPredicateDefinition(PredicateDefinition predicateDefinition, + ModelStoreBuilder storeBuilder) { var partialRelation = getPartialRelation(predicateDefinition); var query = queryCompiler.toQuery(partialRelation.name(), predicateDefinition); + List parameterTypes = null; boolean mutable; TruthValue defaultValue; - if (predicateDefinition.isShadow()) { + if (predicateDefinition.getKind() == PredicateKind.SHADOW) { mutable = false; defaultValue = TruthValue.UNKNOWN; } else { mutable = targetTypes.contains(partialRelation) || isActionTarget(predicateDefinition); - if (predicateDefinition.isError()) { + if (predicateDefinition.getKind() == PredicateKind.ERROR) { defaultValue = TruthValue.FALSE; } else { var seed = modelSeed.getSeed(partialRelation); @@ -614,8 +625,9 @@ private void collectPredicateDefinition(PredicateDefinition predicateDefinition, // The symbol should be mutable if there is at least one non-default entry in the seed. mutable = mutable || cursor.move(); } + parameterTypes = getParameterTypes(predicateDefinition, null); } - var translator = new PredicateTranslator(partialRelation, query, mutable, defaultValue); + var translator = new PredicateTranslator(partialRelation, query, parameterTypes, mutable, defaultValue); storeBuilder.with(translator); } @@ -628,6 +640,28 @@ private boolean isActionTarget(PredicateDefinition predicateDefinition) { return false; } + private List getParameterTypes(ParametricDefinition parametricDefinition, + PartialRelation defaultType) { + var parameters = parametricDefinition.getParameters(); + var parameterTypes = new ArrayList(parameters.size()); + for (var parameter : parameters) { + var relation = parameter.getParameterType(); + parameterTypes.add(relation == null ? defaultType : getPartialRelation(relation)); + } + return Collections.unmodifiableList(parameterTypes); + } + + private void collectBasePredicateDefinition(PredicateDefinition predicateDefinition, + ModelStoreBuilder storeBuilder) { + var partialRelation = getPartialRelation(predicateDefinition); + var parameterTypes = getParameterTypes(predicateDefinition, nodeRelation); + var seed = modelSeed.getSeed(partialRelation); + var defaultValue = seed.majorityValue() == TruthValue.FALSE ? TruthValue.FALSE : TruthValue.UNKNOWN; + boolean partial = predicateDefinition.getKind() == PredicateKind.PARTIAL; + var translator = new BasePredicateTranslator(partialRelation, parameterTypes, defaultValue, partial); + storeBuilder.with(translator); + } + private void collectScopes() { for (var importedProblem : importedProblems) { for (var statement : importedProblem.getStatements()) { diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/BasePredicateDetail.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/BasePredicateDetail.java new file mode 100644 index 000000000..6ffaf267a --- /dev/null +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/BasePredicateDetail.java @@ -0,0 +1,10 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.language.web.semantics.metadata; + +public record BasePredicateDetail() implements RelationDetail { + public static final BasePredicateDetail INSTANCE = new BasePredicateDetail(); +} diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java index 29f8ab8ba..11f0a2287 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/MetadataCreator.java @@ -156,7 +156,10 @@ private RelationDetail getEnumDetail() { } private RelationDetail getPredicateDetail(PredicateDefinition predicate) { - return PredicateDetail.ofError(predicate.isError()); + if (ProblemUtil.isBasePredicate(predicate)) { + return BasePredicateDetail.INSTANCE; + } + return PredicateDetail.ofError(predicate.getKind() == PredicateKind.ERROR); } private QualifiedName getQualifiedName(EObject eObject) { diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/RelationDetail.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/RelationDetail.java index bbe563cdd..00177858d 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/RelationDetail.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/metadata/RelationDetail.java @@ -6,5 +6,5 @@ package tools.refinery.language.web.semantics.metadata; public sealed interface RelationDetail permits ClassDetail, ReferenceDetail, PredicateDetail, OppositeReferenceDetail, - BuiltInDetail { + BuiltInDetail, BasePredicateDetail { } diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java index 818bd80e3..13ae12219 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java @@ -37,7 +37,8 @@ public class XtextWebSocket implements ResponseHandler { .registerSubtype(ReferenceDetail.class, "reference") .registerSubtype(OppositeReferenceDetail.class, "opposite") .registerSubtype(PredicateDetail.class, "predicate") - .registerSubtype(BuiltInDetail.class, "builtin")) + .registerSubtype(BuiltInDetail.class, "builtin") + .registerSubtype(BasePredicateDetail.class, "base")) .create(); private final TransactionExecutor executor; diff --git a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext index 7e3b3c83b..7cd62dcd2 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext +++ b/subprojects/language/src/main/java/tools/refinery/language/Problem.xtext @@ -57,9 +57,14 @@ ReferenceDeclaration: ReferenceMultiplicity returns Multiplicity: "[" Multiplicity "]"; +enum ErrorPredicateKind returns PredicateKind: + ERROR="error"; + +enum PredicateKind: + ERROR="error" | PARTIAL="partial" | SHADOW="shadow"; + PredicateDefinition: - shadow?="shadow"? - ("pred" | error?="error" "pred"?) + (kind=ErrorPredicateKind | kind=PredicateKind? "pred") name=Identifier "(" (parameters+=Parameter ("," parameters+=Parameter)*)? ")" ("<->" bodies+=Conjunction (";" bodies+=Conjunction)*)? @@ -294,7 +299,7 @@ Identifier: NonContainmentIdentifier: ID | "atom" | "multi" | "contained" | "problem" | "module" | - "datatype" | "aggregator" | "decision" | "propagation" | "computed"; + "datatype" | "aggregator" | "decision" | "propagation" | "shadow"; Real returns ecore::EDouble: EXPONENTIAL | INT "." (INT | EXPONENTIAL); diff --git a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java index 44f555638..97e93d462 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java +++ b/subprojects/language/src/main/java/tools/refinery/language/resource/state/ProblemDerivedStateComputer.java @@ -102,7 +102,7 @@ protected void installOrRemoveInvalidMultiplicityPredicate( if (declaration.getInvalidMultiplicity() == null) { var invalidMultiplicity = adapter.createInvalidMultiplicityPredicateIfAbsent(declaration, key -> { var predicate = ProblemFactory.eINSTANCE.createPredicateDefinition(); - predicate.setError(true); + predicate.setKind(PredicateKind.ERROR); predicate.setName("invalidMultiplicity"); var parameter = ProblemFactory.eINSTANCE.createParameter(); parameter.setParameterType(containingClassDeclaration); @@ -125,7 +125,7 @@ protected void installOrRemoveComputedValuePredicate(Adapter adapter, PredicateD if (ProblemUtil.hasComputedValue(predicateDefinition)) { var computedValue = adapter.createComputedValuePredicateIfAbsent(predicateDefinition, key -> { var predicate = ProblemFactory.eINSTANCE.createPredicateDefinition(); - predicate.setShadow(true); + predicate.setKind(PredicateKind.SHADOW); predicate.setName("computed"); return predicate; }); diff --git a/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java b/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java index f1e5a7acf..a8e3a783a 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java +++ b/subprojects/language/src/main/java/tools/refinery/language/typesystem/TypedModule.java @@ -75,7 +75,7 @@ private void checkTypes(Problem problem) { private void checkTypes(PredicateDefinition predicateDefinition) { for (var conjunction : predicateDefinition.getBodies()) { for (var literal : conjunction.getLiterals()) { - coerceIntoLiteral(literal, predicateDefinition.isShadow()); + coerceIntoLiteral(literal, predicateDefinition.getKind() == PredicateKind.SHADOW); } } } diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java index 067e684af..633e9d30e 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java @@ -50,17 +50,19 @@ public static boolean isImplicit(EObject eObject) { } public static boolean isError(EObject eObject) { - return eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isError(); + return eObject instanceof PredicateDefinition predicateDefinition && + predicateDefinition.getKind() == PredicateKind.ERROR; } public static boolean isShadow(EObject eObject) { - return eObject instanceof PredicateDefinition predicateDefinition && predicateDefinition.isShadow(); + return eObject instanceof PredicateDefinition predicateDefinition && + predicateDefinition.getKind() == PredicateKind.SHADOW; } public static boolean mayReferToShadow(EObject context) { var definitionContext = EcoreUtil2.getContainerOfType(context, ParametricDefinition.class); return switch (definitionContext) { - case PredicateDefinition predicateDefinition -> predicateDefinition.isShadow(); + case PredicateDefinition predicateDefinition -> predicateDefinition.getKind() == PredicateKind.SHADOW; case RuleDefinition ignored -> true; case null, default -> false; }; @@ -112,8 +114,16 @@ public static boolean hasMultiplicityConstraint(ReferenceDeclaration referenceDe return true; } + public static boolean isBasePredicate(PredicateDefinition predicateDefinition) { + return switch (predicateDefinition.getKind()) { + case DEFAULT -> predicateDefinition.getBodies().isEmpty(); + case PARTIAL -> true; + default -> false; + }; + } + public static boolean hasComputedValue(PredicateDefinition predicateDefinition) { - return !predicateDefinition.isShadow() && !predicateDefinition.getBodies().isEmpty(); + return predicateDefinition.getKind() != PredicateKind.SHADOW && !predicateDefinition.getBodies().isEmpty(); } public static boolean isTypeLike(Relation relation) { diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java index ace9f3ddb..dbb21e089 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java +++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java @@ -379,15 +379,6 @@ public void checkReferenceType(ReferenceDeclaration referenceDeclaration) { } } - @Check - public void checkPredicateDefinition(PredicateDefinition predicateDefinition) { - if (predicateDefinition.isError() && predicateDefinition.isShadow()) { - var message = "Shadow predicates cannot be marked as error predicates."; - acceptError(message, predicateDefinition, ProblemPackage.Literals.PREDICATE_DEFINITION__ERROR, 0, - SHADOW_RELATION_ISSUE); - } - } - @Check public void checkParameter(Parameter parameter) { checkArity(parameter, ProblemPackage.Literals.PARAMETER__PARAMETER_TYPE, 1); diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java index 7a9599915..4471b1938 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/DirectedCrossReferenceRefiner.java @@ -21,9 +21,9 @@ class DirectedCrossReferenceRefiner extends ConcreteSymbolRefiner sourceRefiner; private PartialInterpretationRefiner targetRefiner; - public DirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol partialSymbol, - Symbol concreteSymbol, PartialRelation sourceType, - PartialRelation targetType) { + protected DirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol partialSymbol, + Symbol concreteSymbol, PartialRelation sourceType, + PartialRelation targetType) { super(adapter, partialSymbol, concreteSymbol); this.sourceType = sourceType; this.targetType = targetType; diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java index 79251d13d..93ed30f69 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java @@ -21,8 +21,9 @@ class UndirectedCrossReferenceRefiner extends ConcreteSymbolRefiner sourceRefiner; - public UndirectedCrossReferenceRefiner(ReasoningAdapter adapter, PartialSymbol partialSymbol, - Symbol concreteSymbol, PartialRelation sourceType) { + protected UndirectedCrossReferenceRefiner(ReasoningAdapter adapter, + PartialSymbol partialSymbol, + Symbol concreteSymbol, PartialRelation sourceType) { super(adapter, partialSymbol, concreteSymbol); this.sourceType = sourceType; } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java new file mode 100644 index 000000000..198bdbfb1 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java @@ -0,0 +1,141 @@ +/* + * SPDX-FileCopyrightText: 2024 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.translator.predicate; + +import tools.refinery.logic.dnf.AbstractQueryBuilder; +import tools.refinery.logic.dnf.Query; +import tools.refinery.logic.literal.Literal; +import tools.refinery.logic.term.NodeVariable; +import tools.refinery.logic.term.truthvalue.TruthValue; +import tools.refinery.store.dse.propagation.PropagationBuilder; +import tools.refinery.store.dse.transition.Rule; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.model.ModelStoreConfiguration; +import tools.refinery.store.query.view.ForbiddenView; +import tools.refinery.store.reasoning.lifting.DnfLifter; +import tools.refinery.store.reasoning.literal.Concreteness; +import tools.refinery.store.reasoning.literal.Modality; +import tools.refinery.store.reasoning.representation.PartialRelation; +import tools.refinery.store.reasoning.translator.PartialRelationTranslator; +import tools.refinery.store.reasoning.translator.RoundingMode; +import tools.refinery.store.reasoning.translator.TranslationException; +import tools.refinery.store.representation.Symbol; + +import java.util.ArrayList; +import java.util.List; + +import static tools.refinery.logic.literal.Literals.not; +import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; +import static tools.refinery.store.reasoning.literal.PartialLiterals.*; +import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; + +public class BasePredicateTranslator implements ModelStoreConfiguration { + private final PartialRelation predicate; + private final List parameterTypes; + private final TruthValue defaultValue; + private final boolean partial; + private final Symbol symbol; + + public BasePredicateTranslator(PartialRelation predicate, List parameterTypes, + TruthValue defaultValue, boolean partial) { + this.predicate = predicate; + this.parameterTypes = parameterTypes; + this.defaultValue = defaultValue; + this.partial = partial; + symbol = Symbol.of(predicate.name(), predicate.arity(), TruthValue.class, defaultValue); + } + + @Override + public void apply(ModelStoreBuilder storeBuilder) { + int arity = predicate.arity(); + if (arity != parameterTypes.size()) { + throw new TranslationException(predicate, + "Expected %d parameter type for base predicate %s, got %d instead" + .formatted(arity, predicate, parameterTypes.size())); + } + if (defaultValue.must()) { + throw new TranslationException(predicate, "Unsupported default value %s for base predicate %s" + .formatted(defaultValue, predicate)); + } + var translator = PartialRelationTranslator.of(predicate); + translator.symbol(symbol); + if (defaultValue.may()) { + configureWithDefaultUnknown(translator); + } else { + configureWithDefaultFalse(storeBuilder); + } + translator.refiner(PredicateRefiner.of(symbol, parameterTypes)); + if (partial) { + translator.roundingMode(RoundingMode.NONE); + } else { + translator.decision(Rule.of(predicate.name(), builder -> { + var parameters = createParameters(builder); + var literals = new ArrayList(arity + 2); + literals.add(may(predicate.call(parameters))); + literals.add(not(candidateMust(predicate.call(parameters)))); + for (int i = 0; i < arity; i++) { + literals.add(not(MULTI_VIEW.call(parameters[i]))); + } + builder.clause(literals); + builder.action(add(predicate, parameters)); + })); + } + storeBuilder.with(translator); + } + + private NodeVariable[] createParameters(AbstractQueryBuilder builder) { + int arity = predicate.arity(); + var parameters = new NodeVariable[arity]; + for (int i = 0; i < arity; i++) { + parameters[i] = builder.parameter("p" + (i + 1)); + } + return parameters; + } + + private void configureWithDefaultUnknown(PartialRelationTranslator translator) { + var name = predicate.name(); + var mayName = DnfLifter.decorateName(name, Modality.MAY, Concreteness.PARTIAL); + int arity = predicate.arity(); + var forbiddenView = new ForbiddenView(symbol); + translator.may(Query.of(mayName, builder -> { + var parameters = createParameters(builder); + var literals = new ArrayList(arity + 1); + for (int i = 0; i < arity; i++) { + literals.add(may(parameterTypes.get(i).call(parameters[i]))); + } + literals.add(not(forbiddenView.call(parameters))); + builder.clause(literals); + })); + if (partial) { + var candidateMayName = DnfLifter.decorateName(name, Modality.MAY, Concreteness.CANDIDATE); + translator.candidateMay(Query.of(candidateMayName, builder -> { + var parameters = createParameters(builder); + var literals = new ArrayList(arity + 1); + for (int i = 0; i < arity; i++) { + literals.add(candidateMay(parameterTypes.get(i).call(parameters[i]))); + } + literals.add(not(forbiddenView.call(parameters))); + builder.clause(literals); + })); + } + } + + private void configureWithDefaultFalse(ModelStoreBuilder storeBuilder) { + var name = predicate.name(); + // Fail if there is no {@link PropagationBuilder}, since it is required for soundness. + var propagationBuilder = storeBuilder.getAdapter(PropagationBuilder.class); + propagationBuilder.rule(Rule.of(name + "#invalidLink", builder -> { + var parameters = createParameters(builder); + int arity = parameters.length; + for (int i = 0; i < arity; i++) { + builder.clause( + may(predicate.call(parameters)), + not(may(parameterTypes.get(i).call(parameters[i]))) + ); + } + })); + } +} diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java new file mode 100644 index 000000000..0fccced25 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateRefiner.java @@ -0,0 +1,94 @@ +/* + * SPDX-FileCopyrightText: 2024 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.translator.predicate; + +import org.jetbrains.annotations.Nullable; +import tools.refinery.logic.term.truthvalue.TruthValue; +import tools.refinery.store.reasoning.ReasoningAdapter; +import tools.refinery.store.reasoning.refinement.ConcreteSymbolRefiner; +import tools.refinery.store.reasoning.refinement.PartialInterpretationRefiner; +import tools.refinery.store.reasoning.representation.PartialRelation; +import tools.refinery.store.reasoning.representation.PartialSymbol; +import tools.refinery.store.reasoning.seed.ModelSeed; +import tools.refinery.store.representation.Symbol; +import tools.refinery.store.tuple.Tuple; + +import java.util.List; +import java.util.Objects; + +class PredicateRefiner extends ConcreteSymbolRefiner { + private final List parameterTypes; + private @Nullable PartialInterpretationRefiner[] parameterTypeRefiners; + + protected PredicateRefiner(ReasoningAdapter adapter, PartialSymbol partialSymbol, + Symbol concreteSymbol, List parameterTypes) { + super(adapter, partialSymbol, concreteSymbol); + this.parameterTypes = parameterTypes; + } + + @Override + public void afterCreate() { + int arity = parameterTypes.size(); + // Generic array creation. + @SuppressWarnings("unchecked") + PartialInterpretationRefiner[] array = new PartialInterpretationRefiner[arity]; + parameterTypeRefiners = array; + var adapter = getAdapter(); + for (int i = 0; i < arity; i++) { + var parameterType = parameterTypes.get(i); + if (parameterType != null) { + array[i] = adapter.getRefiner(parameterType); + } + } + } + + @Override + public boolean merge(Tuple key, TruthValue value) { + var currentValue = get(key); + var mergedValue = currentValue.meet(value); + if (!Objects.equals(currentValue, mergedValue)) { + put(key, mergedValue); + } + // Avoid cyclic propagation between parameter types by avoiding propagation after reaching a fixed point. + if (mergedValue.must() && !currentValue.must()) { + return refineParameters(key); + } + return true; + } + + @Override + public void afterInitialize(ModelSeed modelSeed) { + var predicate = getPartialSymbol(); + var cursor = modelSeed.getCursor(predicate); + while (cursor.move()) { + var value = cursor.getValue(); + if (value.must()) { + var key = cursor.getKey(); + if (!refineParameters(key)) { + throw new IllegalArgumentException("Failed to merge parameter types of predicate %s for key %s" + .formatted(predicate, key)); + } + } + } + } + + private boolean refineParameters(Tuple key) { + int arity = parameterTypeRefiners.length; + for (int i = 0; i < arity; i++) { + var refiner = parameterTypeRefiners[i]; + if (refiner != null && !refiner.merge(Tuple.of(key.get(i)), TruthValue.TRUE)) { + return false; + } + } + return true; + } + + public static Factory of(Symbol concreteSymbol, + List parameterTypes) { + return (adapter, partialSymbol) -> new PredicateRefiner(adapter, partialSymbol, concreteSymbol, + parameterTypes); + } +} diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java index 010ce9771..8dd9cff63 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/PredicateTranslator.java @@ -21,6 +21,9 @@ import tools.refinery.store.reasoning.translator.TranslationException; import tools.refinery.store.representation.Symbol; +import java.util.List; +import java.util.Objects; + import static tools.refinery.logic.literal.Literals.not; import static tools.refinery.store.reasoning.literal.PartialLiterals.may; import static tools.refinery.store.reasoning.literal.PartialLiterals.must; @@ -30,9 +33,11 @@ public class PredicateTranslator implements ModelStoreConfiguration { private final RelationalQuery query; private final boolean mutable; private final TruthValue defaultValue; + private final List parameterTypes; - public PredicateTranslator(PartialRelation relation, RelationalQuery query, boolean mutable, - TruthValue defaultValue) { + public PredicateTranslator(PartialRelation relation, RelationalQuery query, List parameterTypes, + boolean mutable, TruthValue defaultValue) { + this.parameterTypes = parameterTypes; if (relation.arity() != query.arity()) { throw new TranslationException(relation, "Expected arity %d query for partial relation %s, got %d instead" .formatted(relation.arity(), relation, query.arity())); @@ -78,6 +83,10 @@ public void apply(ModelStoreBuilder storeBuilder) { .clause(mayLiterals) .build(); translator.may(may); + + if (parameterTypes != null && parameterTypes.stream().anyMatch(Objects::nonNull)) { + translator.refiner(PredicateRefiner.of(symbol, parameterTypes)); + } } else if (defaultValue.may()) { // If all values are permitted, we don't need to check for any forbidden values in the model. // If the result of this predicate of {@code ERROR}, some other partial relation (that we check for) From 127b88a770082bbb2d41b474ecc76a11bf7ff5d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Thu, 1 Aug 2024 19:25:43 +0200 Subject: [PATCH 03/10] feat(language): base predicate validation --- .../refinery/language/utils/ProblemUtil.java | 13 +++++++-- .../language/validation/ProblemValidator.java | 27 +++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java index 633e9d30e..dcd1651cb 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java @@ -114,6 +114,15 @@ public static boolean hasMultiplicityConstraint(ReferenceDeclaration referenceDe return true; } + public static boolean isDerivedStatePredicate(PredicateDefinition predicateDefinition) { + if (predicateDefinition == null || isBuiltIn(predicateDefinition)) { + return true; + } + var containingFeature = predicateDefinition.eContainingFeature(); + return containingFeature == ProblemPackage.Literals.REFERENCE_DECLARATION__INVALID_MULTIPLICITY || + containingFeature == ProblemPackage.Literals.PREDICATE_DEFINITION__COMPUTED_VALUE; + } + public static boolean isBasePredicate(PredicateDefinition predicateDefinition) { return switch (predicateDefinition.getKind()) { case DEFAULT -> predicateDefinition.getBodies().isEmpty(); @@ -123,7 +132,7 @@ public static boolean isBasePredicate(PredicateDefinition predicateDefinition) { } public static boolean hasComputedValue(PredicateDefinition predicateDefinition) { - return predicateDefinition.getKind() != PredicateKind.SHADOW && !predicateDefinition.getBodies().isEmpty(); + return predicateDefinition.getKind() != PredicateKind.SHADOW && !isBasePredicate(predicateDefinition); } public static boolean isTypeLike(Relation relation) { @@ -140,7 +149,7 @@ public static boolean isTypeLike(Relation relation) { public static boolean isContainmentReference(ReferenceDeclaration referenceDeclaration) { return referenceDeclaration.getKind() == ReferenceKind.CONTAINMENT; } - +; public static boolean isContainerReference(ReferenceDeclaration referenceDeclaration) { var kind = referenceDeclaration.getKind(); if (kind == null) { diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java index dbb21e089..87fa7a34f 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java +++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java @@ -47,6 +47,7 @@ public class ProblemValidator extends AbstractProblemValidator { public static final String INVALID_REFERENCE_TYPE_ISSUE = ISSUE_PREFIX + "INVALID_REFERENCE_TYPE"; public static final String INVALID_ARITY_ISSUE = ISSUE_PREFIX + "INVALID_ARITY"; public static final String INVALID_MODALITY_ISSUE = ISSUE_PREFIX + "INVALID_MODALITY"; + public static final String INVALID_PREDICATE_ISSUE = ISSUE_PREFIX + "INVALID_PREDICATE"; public static final String INVALID_RULE_ISSUE = ISSUE_PREFIX + "INVALID_RULE"; public static final String INVALID_TRANSITIVE_CLOSURE_ISSUE = ISSUE_PREFIX + "INVALID_TRANSITIVE_CLOSURE"; public static final String SHADOW_RELATION_ISSUE = ISSUE_PREFIX + "SHADOW_RELATION"; @@ -379,6 +380,32 @@ public void checkReferenceType(ReferenceDeclaration referenceDeclaration) { } } + @Check + public void checkPredicateDefinition(PredicateDefinition predicateDefinition) { + if (ProblemUtil.isDerivedStatePredicate(predicateDefinition)) { + return; + } + String message = null; + if (ProblemUtil.isBasePredicate(predicateDefinition)) { + if (!predicateDefinition.getBodies().isEmpty()) { + var predicateType = predicateDefinition.getKind() == PredicateKind.PARTIAL ? "Partial base predicate" : + "Base predicate"; + message = "%s '%s' must not have any clauses.".formatted(predicateType, predicateDefinition.getName()); + } + } else if (predicateDefinition.getBodies().isEmpty()) { + var predicateType = switch (predicateDefinition.getKind()) { + case ERROR -> "Error predicate"; + case SHADOW -> "Shadow predicate"; + default -> "Predicate"; + }; + message = "%s '%s' must have at least one clause.".formatted(predicateType, predicateDefinition.getName()); + } + if (message != null) { + acceptError(message, predicateDefinition, ProblemPackage.Literals.NAMED_ELEMENT__NAME, 0, + INVALID_PREDICATE_ISSUE); + } + } + @Check public void checkParameter(Parameter parameter) { checkArity(parameter, ProblemPackage.Literals.PARAMETER__PARAMETER_TYPE, 1); From 02261dc368cb0c9e5ed681ad6a26d955e82465dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Thu, 1 Aug 2024 20:08:28 +0200 Subject: [PATCH 04/10] fix(reasoning): default false base predicvate translation --- .../main/java/tools/refinery/language/utils/ProblemUtil.java | 5 +---- .../tools/refinery/language/validation/ProblemValidator.java | 2 +- .../tools/refinery/store/dse/transition/RuleBuilder.java | 3 +++ .../translator/predicate/BasePredicateTranslator.java | 4 +++- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java index dcd1651cb..55b0ca604 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java @@ -115,9 +115,6 @@ public static boolean hasMultiplicityConstraint(ReferenceDeclaration referenceDe } public static boolean isDerivedStatePredicate(PredicateDefinition predicateDefinition) { - if (predicateDefinition == null || isBuiltIn(predicateDefinition)) { - return true; - } var containingFeature = predicateDefinition.eContainingFeature(); return containingFeature == ProblemPackage.Literals.REFERENCE_DECLARATION__INVALID_MULTIPLICITY || containingFeature == ProblemPackage.Literals.PREDICATE_DEFINITION__COMPUTED_VALUE; @@ -149,7 +146,7 @@ public static boolean isTypeLike(Relation relation) { public static boolean isContainmentReference(ReferenceDeclaration referenceDeclaration) { return referenceDeclaration.getKind() == ReferenceKind.CONTAINMENT; } -; + public static boolean isContainerReference(ReferenceDeclaration referenceDeclaration) { var kind = referenceDeclaration.getKind(); if (kind == null) { diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java index 87fa7a34f..74a6a0fc8 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java +++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java @@ -382,7 +382,7 @@ public void checkReferenceType(ReferenceDeclaration referenceDeclaration) { @Check public void checkPredicateDefinition(PredicateDefinition predicateDefinition) { - if (ProblemUtil.isDerivedStatePredicate(predicateDefinition)) { + if (ProblemUtil.isBuiltIn(predicateDefinition) || ProblemUtil.isDerivedStatePredicate(predicateDefinition)) { return; } String message = null; diff --git a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java index c2e43e0d4..b38774b59 100644 --- a/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java +++ b/subprojects/store-dse/src/main/java/tools/refinery/store/dse/transition/RuleBuilder.java @@ -65,6 +65,9 @@ public RuleBuilder action(ActionCallback4 callback) { } public Rule build() { + if (action == null) { + throw new IllegalStateException("Rule '%s' has no action".formatted(name)); + } var precondition = dnfBuilder.build().asRelation(); return new Rule(name, precondition, Action.ofPrecondition(precondition, action)); } diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java index 198bdbfb1..fb4521dda 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/BasePredicateTranslator.java @@ -29,6 +29,7 @@ import static tools.refinery.logic.literal.Literals.not; import static tools.refinery.store.reasoning.actions.PartialActionLiterals.add; +import static tools.refinery.store.reasoning.actions.PartialActionLiterals.remove; import static tools.refinery.store.reasoning.literal.PartialLiterals.*; import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.MULTI_VIEW; @@ -127,7 +128,7 @@ private void configureWithDefaultFalse(ModelStoreBuilder storeBuilder) { var name = predicate.name(); // Fail if there is no {@link PropagationBuilder}, since it is required for soundness. var propagationBuilder = storeBuilder.getAdapter(PropagationBuilder.class); - propagationBuilder.rule(Rule.of(name + "#invalidLink", builder -> { + propagationBuilder.rule(Rule.of(name + "#invalid", builder -> { var parameters = createParameters(builder); int arity = parameters.length; for (int i = 0; i < arity; i++) { @@ -136,6 +137,7 @@ private void configureWithDefaultFalse(ModelStoreBuilder storeBuilder) { not(may(parameterTypes.get(i).call(parameters[i]))) ); } + builder.action(remove(predicate, parameters)); })); } } From 9979d14426588d1d867cb63073262003e5053adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Fri, 2 Aug 2024 16:21:29 +0200 Subject: [PATCH 05/10] fix(semantics): handle empty models --- .../tools/refinery/store/reasoning/seed/MapBasedSeed.java | 4 ++++ 1 file changed, 4 insertions(+) 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 index 3b78db02e..031115749 100644 --- 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 @@ -64,6 +64,10 @@ public boolean isTerminated() { public boolean move() { return switch (state) { case INITIAL -> { + if (nodeCount == 0) { + state = State.TERMINATED; + yield false; + } state = State.STARTED; yield checkValue() || moveToNext(); } From 7720586b4f10f4cd3c011a672a60e26ca30ae2ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Fri, 2 Aug 2024 21:06:50 +0200 Subject: [PATCH 06/10] fix(reasoning): undirected reference refinement --- .../undirectedMultiplicity.problem | 57 +++++++++++++++++++ .../UndirectedCrossReferenceRefiner.java | 8 ++- 2 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/undirectedMultiplicity.problem diff --git a/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/undirectedMultiplicity.problem b/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/undirectedMultiplicity.problem new file mode 100644 index 000000000..bd9fd1476 --- /dev/null +++ b/subprojects/generator/src/test/resources/tools/refinery/generator/crossreference/undirectedMultiplicity.problem @@ -0,0 +1,57 @@ +% Copyright (c) 2024 The Refinery Authors +% +% SPDX-License-Identifier: EPL-2.0 + +% TEST: upper bound propagation + +class Person { + Person[0..2] friend opposite friend +} + +friend(a, b). +friend(a, c). +friend(b, c). +!exists(Person::new). + +% EXPECT: +friend(b, a). +friend(c, a). +friend(c, b). +!friend(a, a). +!friend(b, b). +!friend(c, c). + +% TEST: lower bound propagation + +class Person { + Person[2..*] friend opposite friend +} + +Person(a). +!friend(a, a). +!friend(b, b). +!friend(c, c). +!exists(Person::new). + +% EXPECT: +friend(a, b). +friend(a, c). +friend(b, c). + +% TEST: upper and lower bound propagation + +class Person { + Person[2] friend opposite friend +} + +friend(a, b). +friend(a, c). +!friend(b, b). +!friend(c, c). +!exists(Person::new). + +% EXPECT: +friend(b, c). +!friend(a, a). +!friend(b, b). +!friend(c, c). diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java index 93ed30f69..116ac7ad6 100644 --- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/crossreference/UndirectedCrossReferenceRefiner.java @@ -41,9 +41,11 @@ public boolean merge(Tuple key, TruthValue value) { var mergedValue = currentValue.meet(value); if (!Objects.equals(currentValue, mergedValue)) { var oldValue = put(key, mergedValue); - var inverseOldValue = put(Tuple.of(target, source), mergedValue); - if (!Objects.equals(oldValue, inverseOldValue)) { - return false; + if (source != target) { + var inverseOldValue = put(Tuple.of(target, source), mergedValue); + if (!Objects.equals(oldValue, inverseOldValue)) { + return false; + } } } if (value.must()) { From 9c17caa8f972a93d01a8381ef4c01c28420d7ffd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Fri, 2 Aug 2024 21:16:15 +0200 Subject: [PATCH 07/10] test(semantics): run both with and without non-existing objects Make sure we test both the ModelSemantics and ModelGenerator configurations. --- .../generator/ModelSemanticsFactory.java | 2 ++ .../generator/FileBasedSemanticsTest.java | 17 ++++++++++++++++- .../generator/tests/DynamicTestLoader.java | 6 +++++- .../generator/tests/SemanticsTestCase.java | 6 +++++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java b/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java index b624a507c..bbb8548b1 100644 --- a/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java +++ b/subprojects/generator/src/main/java/tools/refinery/generator/ModelSemanticsFactory.java @@ -15,6 +15,8 @@ import java.util.Collection; import java.util.Set; +// This class is used as a fluent builder, so it's not necessary to use the return value of all of its methods. +@SuppressWarnings("UnusedReturnValue") public final class ModelSemanticsFactory extends ModelFacadeFactory { private boolean withCandidateInterpretations; diff --git a/subprojects/generator/src/test/java/tools/refinery/generator/FileBasedSemanticsTest.java b/subprojects/generator/src/test/java/tools/refinery/generator/FileBasedSemanticsTest.java index 6e954188a..b5d2ae913 100644 --- a/subprojects/generator/src/test/java/tools/refinery/generator/FileBasedSemanticsTest.java +++ b/subprojects/generator/src/test/java/tools/refinery/generator/FileBasedSemanticsTest.java @@ -6,6 +6,7 @@ package tools.refinery.generator; import com.google.inject.Inject; +import com.google.inject.Provider; import org.junit.jupiter.api.DynamicNode; import org.junit.jupiter.api.TestFactory; import tools.refinery.generator.tests.DynamicTestLoader; @@ -18,8 +19,22 @@ class FileBasedSemanticsTest { @Inject private DynamicTestLoader loader; + @Inject + private Provider semanticsFactoryProvider; + @TestFactory - Stream fileBasedTests() { + Stream testWithNonExistingObjects() { + return getFileBasedTests(true); + } + + @TestFactory + Stream testWithoutNonExistingObjects() { + return getFileBasedTests(false); + } + + private Stream getFileBasedTests(boolean keepNonExistingObjects) { + loader.setSemanticsFactoryProvider(() -> semanticsFactoryProvider.get() + .keepNonExistingObjects(keepNonExistingObjects)); return loader.allFromClasspath(getClass()); } } diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/DynamicTestLoader.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/DynamicTestLoader.java index 7886ec0ae..001ee3a4d 100644 --- a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/DynamicTestLoader.java +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/DynamicTestLoader.java @@ -34,9 +34,13 @@ public class DynamicTestLoader { @Inject private Provider testLoaderProvider; - @Inject private Provider semanticsFactoryProvider; + @Inject + public void setSemanticsFactoryProvider(Provider semanticsFactoryProvider) { + this.semanticsFactoryProvider = semanticsFactoryProvider; + } + public Stream allFromClasspath(Class contextClass) { var paths = getExtraPaths(contextClass); if (paths.isEmpty()) { diff --git a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestCase.java b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestCase.java index 952924e2e..f75cac8e3 100644 --- a/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestCase.java +++ b/subprojects/generator/src/testFixtures/java/tools/refinery/generator/tests/SemanticsTestCase.java @@ -8,12 +8,14 @@ import org.eclipse.collections.api.factory.primitive.IntObjectMaps; import org.eclipse.collections.api.map.primitive.IntObjectMap; import org.eclipse.core.runtime.AssertionFailedException; +import tools.refinery.generator.FilteredInterpretation; import tools.refinery.generator.ModelSemantics; import tools.refinery.generator.ModelSemanticsFactory; import tools.refinery.language.model.problem.Problem; import tools.refinery.language.semantics.ProblemTrace; import tools.refinery.logic.term.truthvalue.TruthValue; import tools.refinery.store.map.Cursor; +import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.literal.Concreteness; import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.tuple.Tuple; @@ -47,8 +49,10 @@ private void checkNoErrors(ModelSemantics semantics) { var errorsBuilder = new StringBuilder("Errors found in partial model:\n\n"); var trace = semantics.getProblemTrace(); IntObjectMap nodeNames = null; + var existsInterpretation = semantics.getPartialInterpretation(ReasoningAdapter.EXISTS_SYMBOL); for (var symbol : trace.getRelationTrace().values()) { - var interpretation = semantics.getPartialInterpretation(symbol); + var interpretation = new FilteredInterpretation<>(semantics.getPartialInterpretation(symbol), + existsInterpretation); var cursor = interpretation.getAll(); while (cursor.move()) { if (!cursor.getValue().isError()) { From afbbaad5e02e561a5aafbd024c0c0541b48165b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Fri, 2 Aug 2024 21:25:15 +0200 Subject: [PATCH 08/10] chore(deps): bump dependencies --- gradle/libs.versions.toml | 2 +- package.json | 6 +- subprojects/docs/package.json | 4 +- subprojects/frontend/package.json | 14 +- yarn.lock | 342 +++++++++++++++--------------- 5 files changed, 184 insertions(+), 184 deletions(-) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3ef535e73..9ba843ea7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -39,7 +39,7 @@ jdt-core = "3.38.0" jdt-debug = "3.21.400" jdt-launching = "3.22.0" jetbrainsAnnotations = "24.1.0" -jetty = "12.0.11" +jetty = "12.0.12" jmh = "1.37" jna = "5.14.0" junit = "5.10.3" diff --git a/package.json b/package.json index 32c0f23c6..478ae120b 100644 --- a/package.json +++ b/package.json @@ -36,10 +36,10 @@ }, "devDependencies": { "@types/eslint": "^8.56.11", - "@types/node": "^20.14.12", + "@types/node": "^20.14.14", "@types/react": "^18.3.3", - "@typescript-eslint/eslint-plugin": "^7.17.0", - "@typescript-eslint/parser": "^7.17.0", + "@typescript-eslint/eslint-plugin": "^7.18.0", + "@typescript-eslint/parser": "^7.18.0", "corepack": "^0.29.3", "eslint": "^8.57.0", "eslint-config-airbnb": "^19.0.4", diff --git a/subprojects/docs/package.json b/subprojects/docs/package.json index 3f8a8cdb2..0ba80c2c5 100644 --- a/subprojects/docs/package.json +++ b/subprojects/docs/package.json @@ -44,7 +44,7 @@ "@fontsource/open-sans": "^5.0.28", "@material-icons/svg": "^1.0.33", "@mdx-js/react": "^3.0.1", - "@swc/core": "^1.7.2", + "@swc/core": "^1.7.5", "clsx": "^2.1.1", "java-properties": "^1.0.2", "mdast-util-mdx": "^3.0.0", @@ -68,7 +68,7 @@ "@docusaurus/types": "^3.4.0", "@types/babel__core": "^7.20.5", "@types/mdast": "^4.0.4", - "@types/node": "^20.14.12", + "@types/node": "^20.14.14", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@types/unist": "^3.0.2" diff --git a/subprojects/frontend/package.json b/subprojects/frontend/package.json index 638b205da..9fa73cc78 100644 --- a/subprojects/frontend/package.json +++ b/subprojects/frontend/package.json @@ -34,7 +34,7 @@ "@codemirror/lint": "^6.8.1", "@codemirror/search": "^6.5.6", "@codemirror/state": "^6.4.1", - "@codemirror/view": "^6.29.0", + "@codemirror/view": "^6.29.1", "@emotion/cache": "^11.13.1", "@emotion/react": "^11.13.0", "@emotion/serialize": "^1.3.0", @@ -46,12 +46,12 @@ "@hpcc-js/wasm": "^2.18.0", "@lezer/common": "^1.2.1", "@lezer/highlight": "^1.2.0", - "@lezer/lr": "^1.4.1", + "@lezer/lr": "^1.4.2", "@material-icons/svg": "^1.0.33", - "@mui/icons-material": "^5.16.5", - "@mui/material": "^5.16.5", - "@mui/system": "^5.16.5", - "@mui/x-data-grid": "^7.11.1", + "@mui/icons-material": "^5.16.6", + "@mui/material": "^5.16.6", + "@mui/system": "^5.16.6", + "@mui/x-data-grid": "^7.12.0", "ansi-styles": "^6.2.1", "csstype": "^3.1.3", "d3": "^7.9.0", @@ -88,7 +88,7 @@ "@types/lodash-es": "^4.17.12", "@types/micromatch": "^4.0.9", "@types/ms": "^0.7.34", - "@types/node": "^20.14.12", + "@types/node": "^20.14.14", "@types/pnpapi": "^0.0.5", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", diff --git a/yarn.lock b/yarn.lock index a1993b47c..d8841bb64 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1710,12 +1710,12 @@ __metadata: languageName: node linkType: hard -"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.24.8, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": - version: 7.24.8 - resolution: "@babel/runtime@npm:7.24.8" +"@babel/runtime@npm:^7.1.2, @babel/runtime@npm:^7.10.3, @babel/runtime@npm:^7.11.2, @babel/runtime@npm:^7.12.13, @babel/runtime@npm:^7.12.5, @babel/runtime@npm:^7.14.0, @babel/runtime@npm:^7.18.3, @babel/runtime@npm:^7.22.6, @babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.25.0, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": + version: 7.25.0 + resolution: "@babel/runtime@npm:7.25.0" dependencies: regenerator-runtime: "npm:^0.14.0" - checksum: 10c0/f24b30af6b3ecae19165b3b032f9bc37b2d1769677bd63b69a6f81061967cfc847aa822518402ea6616b1d301d7eb46986b99c9f69cdb5880834fca2e6b34881 + checksum: 10c0/bd3faf246170826cef2071a94d7b47b49d532351360ecd17722d03f6713fd93a3eb3dbd9518faa778d5e8ccad7392a7a604e56bd37aaad3f3aa68d619ccd983d languageName: node linkType: hard @@ -1831,14 +1831,14 @@ __metadata: languageName: node linkType: hard -"@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.29.0": - version: 6.29.0 - resolution: "@codemirror/view@npm:6.29.0" +"@codemirror/view@npm:^6.0.0, @codemirror/view@npm:^6.17.0, @codemirror/view@npm:^6.23.0, @codemirror/view@npm:^6.27.0, @codemirror/view@npm:^6.29.1": + version: 6.29.1 + resolution: "@codemirror/view@npm:6.29.1" dependencies: "@codemirror/state": "npm:^6.4.0" style-mod: "npm:^4.1.0" w3c-keyname: "npm:^2.2.4" - checksum: 10c0/155b58bfe7de5c91a4dd3877cc2b23e1e2f796de0bf59d01c46c0797126e70052f819465b2467e1558b2cfaac25c9569b53eef09fcc5a1ae1a432ab8019e364f + checksum: 10c0/f5cc5aed853ab5c0ca2fcb3337b7332650bcbc403faddd436fd7d57bc811901b28fd505bd9301e067b2fefd7f36382f0f67977c2866b2b5b905c01e4ef666476 languageName: node linkType: hard @@ -3071,12 +3071,12 @@ __metadata: languageName: node linkType: hard -"@lezer/lr@npm:^1.0.0, @lezer/lr@npm:^1.3.0, @lezer/lr@npm:^1.4.1": - version: 1.4.1 - resolution: "@lezer/lr@npm:1.4.1" +"@lezer/lr@npm:^1.0.0, @lezer/lr@npm:^1.3.0, @lezer/lr@npm:^1.4.2": + version: 1.4.2 + resolution: "@lezer/lr@npm:1.4.2" dependencies: "@lezer/common": "npm:^1.0.0" - checksum: 10c0/e24a383c52248321035d8157d3271890a5740e7a324f7026f1cb7556d3bd9883edeb53df194a8a3f7de50ca034112b234e31211a6b235d9d8d7791a0319b1724 + checksum: 10c0/22bb5d0d4b33d0de5eb0706b7e5b5f2d20f570e112d9110009bd35b62ff10f2eb4eff8da4cf373dd4ddf5e06a304120b8f039add7ed9997c981c13945d5329cd languageName: node linkType: hard @@ -3130,16 +3130,16 @@ __metadata: languageName: node linkType: hard -"@mui/core-downloads-tracker@npm:^5.16.5": - version: 5.16.5 - resolution: "@mui/core-downloads-tracker@npm:5.16.5" - checksum: 10c0/f8052e42e0694b4c95b0edf1893325417ac76fbe9b7714ddad56f607f251d5edbdecf3e7ae34b28ecd7e6d9eee4d3f958d3ff743db98cb35d4e69eef30dd0e23 +"@mui/core-downloads-tracker@npm:^5.16.6": + version: 5.16.6 + resolution: "@mui/core-downloads-tracker@npm:5.16.6" + checksum: 10c0/ee7655eda56e8eeb18ae600b24182c10caab4468d33179de3e76a783a827be9577afe114750835f7c9c7f555c0de0845c0500e91ed2e669b16399a1ffe41c33d languageName: node linkType: hard -"@mui/icons-material@npm:^5.16.5": - version: 5.16.5 - resolution: "@mui/icons-material@npm:5.16.5" +"@mui/icons-material@npm:^5.16.6": + version: 5.16.6 + resolution: "@mui/icons-material@npm:5.16.6" dependencies: "@babel/runtime": "npm:^7.23.9" peerDependencies: @@ -3149,19 +3149,19 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/6ba9cbb5bc7a5d3fbc297683973c7c15c4a95f8b12cf55cadd069d4ecbaf5639bb9044da82ddc029c04203ee81a8ac691ea5ada769dc86fef3f25a13fcec44fe + checksum: 10c0/9d24d95e8b4606b5fa2a2e40319255e2e6da52845db50d2d3ca6fd526102be3c498ec1aa9be8d33aad52cf531749b15d490abca5f23af92afc86cf45dc62ce24 languageName: node linkType: hard -"@mui/material@npm:^5.16.5": - version: 5.16.5 - resolution: "@mui/material@npm:5.16.5" +"@mui/material@npm:^5.16.6": + version: 5.16.6 + resolution: "@mui/material@npm:5.16.6" dependencies: "@babel/runtime": "npm:^7.23.9" - "@mui/core-downloads-tracker": "npm:^5.16.5" - "@mui/system": "npm:^5.16.5" + "@mui/core-downloads-tracker": "npm:^5.16.6" + "@mui/system": "npm:^5.16.6" "@mui/types": "npm:^7.2.15" - "@mui/utils": "npm:^5.16.5" + "@mui/utils": "npm:^5.16.6" "@popperjs/core": "npm:^2.11.8" "@types/react-transition-group": "npm:^4.4.10" clsx: "npm:^2.1.0" @@ -3182,16 +3182,16 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10c0/d147f4c0b8b7cb640da2c4e2511bb8a8dd4152caaf49dc4f380dc37bac4960722b30e7fc310f3339e34a560a3c873f764cac6c37295407ca1f771d4e0854febb + checksum: 10c0/52cbffd87a36b9f8e16ba59030e89501e680ddea4d3dcf3db5d8fb7b86205444b00162e656c79571824a74769f879f39df2e7671d1e7ca8bc23602a7bf588dc5 languageName: node linkType: hard -"@mui/private-theming@npm:^5.16.5": - version: 5.16.5 - resolution: "@mui/private-theming@npm:5.16.5" +"@mui/private-theming@npm:^5.16.6": + version: 5.16.6 + resolution: "@mui/private-theming@npm:5.16.6" dependencies: "@babel/runtime": "npm:^7.23.9" - "@mui/utils": "npm:^5.16.5" + "@mui/utils": "npm:^5.16.6" prop-types: "npm:^15.8.1" peerDependencies: "@types/react": ^17.0.0 || ^18.0.0 @@ -3199,13 +3199,13 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/4850ff47b5a4b1b19356fbd30ed675db6f688eda02bebf16d8b23bebc67bd55baaf4fdba1acae7d5b5e6bbdc2ac0f01c1c85005886b1ba098df65a9b26d4d4c7 + checksum: 10c0/0a09afd6c2be37197973a856049f97e2f17f3e5e6cf6387af036055342efbfcd7d7066dcad587886f25f491e5940e4e9bb7d732d5099eb85b53b84ef120e9555 languageName: node linkType: hard -"@mui/styled-engine@npm:^5.16.4": - version: 5.16.4 - resolution: "@mui/styled-engine@npm:5.16.4" +"@mui/styled-engine@npm:^5.16.6": + version: 5.16.6 + resolution: "@mui/styled-engine@npm:5.16.6" dependencies: "@babel/runtime": "npm:^7.23.9" "@emotion/cache": "npm:^11.11.0" @@ -3220,19 +3220,19 @@ __metadata: optional: true "@emotion/styled": optional: true - checksum: 10c0/5dbd656aedfbf308dbbbb651a589c12b85ba1af2b6b351a657ee5af1210eab9629cee0781b2dc33b14b0a9482f7a6fa6e8b695d94f7cc5f1f7df7efe88034372 + checksum: 10c0/b15e653c8756059c8ae2891ca54900573e22f6ed1aaf65a389ec838f2aca3252aeeb9a79aec4a43f080152b161a416e60b31a62595ba86ad5f72eda5642caaf2 languageName: node linkType: hard -"@mui/system@npm:^5.16.5": - version: 5.16.5 - resolution: "@mui/system@npm:5.16.5" +"@mui/system@npm:^5.16.5, @mui/system@npm:^5.16.6": + version: 5.16.6 + resolution: "@mui/system@npm:5.16.6" dependencies: "@babel/runtime": "npm:^7.23.9" - "@mui/private-theming": "npm:^5.16.5" - "@mui/styled-engine": "npm:^5.16.4" + "@mui/private-theming": "npm:^5.16.6" + "@mui/styled-engine": "npm:^5.16.6" "@mui/types": "npm:^7.2.15" - "@mui/utils": "npm:^5.16.5" + "@mui/utils": "npm:^5.16.6" clsx: "npm:^2.1.0" csstype: "npm:^3.1.3" prop-types: "npm:^15.8.1" @@ -3248,7 +3248,7 @@ __metadata: optional: true "@types/react": optional: true - checksum: 10c0/af3eccfa96e78e00a8b5a71c025b57baeb85b02adb44a7e1e96779ddcb3f0070d9880dcb6b8a7bdb78339fa1bbf892b63ef80b81d0497c9bbd45f5ea14958c91 + checksum: 10c0/493900594f51d1fc84994042e5cfa000b2d7664f86c8c0d62b87dbbb51cf7e789a700512f98e18c82e605beab38c20d0714ec25c46e2f6a5024f79f16db743f7 languageName: node linkType: hard @@ -3264,9 +3264,9 @@ __metadata: languageName: node linkType: hard -"@mui/utils@npm:^5.16.5": - version: 5.16.5 - resolution: "@mui/utils@npm:5.16.5" +"@mui/utils@npm:^5.16.5, @mui/utils@npm:^5.16.6": + version: 5.16.6 + resolution: "@mui/utils@npm:5.16.6" dependencies: "@babel/runtime": "npm:^7.23.9" "@mui/types": "npm:^7.2.15" @@ -3280,18 +3280,18 @@ __metadata: peerDependenciesMeta: "@types/react": optional: true - checksum: 10c0/8b51190232c192db7dfd9fd33e054a789bf162d7ccfb40ee21d6b2636f07aa69d60e3f5bdc6ded1b3520fa4f93134101a5c4158772f589e56eff3a04f1a59a10 + checksum: 10c0/2db3d11a83d7216fb8ceb459d4b30c795922c04cd8fabc26c721dd7b4f5ed5c4f3f3ace6ea70227bf3b79361bd58f13b723562cfd40255424d979ab238ab2e91 languageName: node linkType: hard -"@mui/x-data-grid@npm:^7.11.1": - version: 7.11.1 - resolution: "@mui/x-data-grid@npm:7.11.1" +"@mui/x-data-grid@npm:^7.12.0": + version: 7.12.0 + resolution: "@mui/x-data-grid@npm:7.12.0" dependencies: - "@babel/runtime": "npm:^7.24.8" + "@babel/runtime": "npm:^7.25.0" "@mui/system": "npm:^5.16.5" "@mui/utils": "npm:^5.16.5" - "@mui/x-internals": "npm:7.11.1" + "@mui/x-internals": "npm:7.12.0" clsx: "npm:^2.1.1" prop-types: "npm:^15.8.1" reselect: "npm:^4.1.8" @@ -3299,19 +3299,19 @@ __metadata: "@mui/material": ^5.15.14 react: ^17.0.0 || ^18.0.0 react-dom: ^17.0.0 || ^18.0.0 - checksum: 10c0/c7bec9c6c9629a8f0d727b3be158eb0835efa6dcb966327ab56608c21147bf5ce4029688b55ce4dcf8c470926942ebc2b8deffed587c621c6dab5a07bb589234 + checksum: 10c0/4ff36033c5fdfa9bcfe70b62e4432e61d204033c999da89590f15afd61f9315a3c35e71628e5d5b7fb4bde7b6686e3230e5754846733ade8b100cda6873bc9fc languageName: node linkType: hard -"@mui/x-internals@npm:7.11.1": - version: 7.11.1 - resolution: "@mui/x-internals@npm:7.11.1" +"@mui/x-internals@npm:7.12.0": + version: 7.12.0 + resolution: "@mui/x-internals@npm:7.12.0" dependencies: - "@babel/runtime": "npm:^7.24.8" + "@babel/runtime": "npm:^7.25.0" "@mui/utils": "npm:^5.16.5" peerDependencies: react: ^17.0.0 || ^18.0.0 - checksum: 10c0/003c325accee117d0037273088d042a1122b4ec27f752847fede68d384d1b77024a6c5b1aa9d642a9757737c7ece79bba494a1325a37c13ce6c11b5a653c116a + checksum: 10c0/7fcb5a7785936c4cee7c8900a639523b0e04257b1ea54383886652801eeb09993a7ecae2fc6a06a699edc68108b82c1b968a56ec916f4530d6fdcfa7009b6d8a languageName: node linkType: hard @@ -3427,10 +3427,10 @@ __metadata: "@fontsource/open-sans": "npm:^5.0.28" "@material-icons/svg": "npm:^1.0.33" "@mdx-js/react": "npm:^3.0.1" - "@swc/core": "npm:^1.7.2" + "@swc/core": "npm:^1.7.5" "@types/babel__core": "npm:^7.20.5" "@types/mdast": "npm:^4.0.4" - "@types/node": "npm:^20.14.12" + "@types/node": "npm:^20.14.14" "@types/react": "npm:^18.3.3" "@types/react-dom": "npm:^18.3.0" "@types/unist": "npm:^3.0.2" @@ -3463,7 +3463,7 @@ __metadata: "@codemirror/lint": "npm:^6.8.1" "@codemirror/search": "npm:^6.5.6" "@codemirror/state": "npm:^6.4.1" - "@codemirror/view": "npm:^6.29.0" + "@codemirror/view": "npm:^6.29.1" "@emotion/cache": "npm:^11.13.1" "@emotion/react": "npm:^11.13.0" "@emotion/serialize": "npm:^1.3.0" @@ -3476,12 +3476,12 @@ __metadata: "@lezer/common": "npm:^1.2.1" "@lezer/generator": "npm:^1.7.1" "@lezer/highlight": "npm:^1.2.0" - "@lezer/lr": "npm:^1.4.1" + "@lezer/lr": "npm:^1.4.2" "@material-icons/svg": "npm:^1.0.33" - "@mui/icons-material": "npm:^5.16.5" - "@mui/material": "npm:^5.16.5" - "@mui/system": "npm:^5.16.5" - "@mui/x-data-grid": "npm:^7.11.1" + "@mui/icons-material": "npm:^5.16.6" + "@mui/material": "npm:^5.16.6" + "@mui/system": "npm:^5.16.6" + "@mui/x-data-grid": "npm:^7.12.0" "@types/d3": "npm:^7.4.3" "@types/d3-color": "npm:^3.1.3" "@types/d3-graphviz": "npm:^2.6.10" @@ -3492,7 +3492,7 @@ __metadata: "@types/lodash-es": "npm:^4.17.12" "@types/micromatch": "npm:^4.0.9" "@types/ms": "npm:^0.7.34" - "@types/node": "npm:^20.14.12" + "@types/node": "npm:^20.14.14" "@types/pnpapi": "npm:^0.0.5" "@types/react": "npm:^18.3.3" "@types/react-dom": "npm:^18.3.0" @@ -3536,10 +3536,10 @@ __metadata: resolution: "@refinery/root@workspace:." dependencies: "@types/eslint": "npm:^8.56.11" - "@types/node": "npm:^20.14.12" + "@types/node": "npm:^20.14.14" "@types/react": "npm:^18.3.3" - "@typescript-eslint/eslint-plugin": "npm:^7.17.0" - "@typescript-eslint/parser": "npm:^7.17.0" + "@typescript-eslint/eslint-plugin": "npm:^7.18.0" + "@typescript-eslint/parser": "npm:^7.18.0" corepack: "npm:^0.29.3" eslint: "npm:^8.57.0" eslint-config-airbnb: "npm:^19.0.4" @@ -3979,90 +3979,90 @@ __metadata: languageName: node linkType: hard -"@swc/core-darwin-arm64@npm:1.7.2": - version: 1.7.2 - resolution: "@swc/core-darwin-arm64@npm:1.7.2" +"@swc/core-darwin-arm64@npm:1.7.5": + version: 1.7.5 + resolution: "@swc/core-darwin-arm64@npm:1.7.5" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@swc/core-darwin-x64@npm:1.7.2": - version: 1.7.2 - resolution: "@swc/core-darwin-x64@npm:1.7.2" +"@swc/core-darwin-x64@npm:1.7.5": + version: 1.7.5 + resolution: "@swc/core-darwin-x64@npm:1.7.5" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@swc/core-linux-arm-gnueabihf@npm:1.7.2": - version: 1.7.2 - resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.2" +"@swc/core-linux-arm-gnueabihf@npm:1.7.5": + version: 1.7.5 + resolution: "@swc/core-linux-arm-gnueabihf@npm:1.7.5" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@swc/core-linux-arm64-gnu@npm:1.7.2": - version: 1.7.2 - resolution: "@swc/core-linux-arm64-gnu@npm:1.7.2" +"@swc/core-linux-arm64-gnu@npm:1.7.5": + version: 1.7.5 + resolution: "@swc/core-linux-arm64-gnu@npm:1.7.5" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-arm64-musl@npm:1.7.2": - version: 1.7.2 - resolution: "@swc/core-linux-arm64-musl@npm:1.7.2" +"@swc/core-linux-arm64-musl@npm:1.7.5": + version: 1.7.5 + resolution: "@swc/core-linux-arm64-musl@npm:1.7.5" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@swc/core-linux-x64-gnu@npm:1.7.2": - version: 1.7.2 - resolution: "@swc/core-linux-x64-gnu@npm:1.7.2" +"@swc/core-linux-x64-gnu@npm:1.7.5": + version: 1.7.5 + resolution: "@swc/core-linux-x64-gnu@npm:1.7.5" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@swc/core-linux-x64-musl@npm:1.7.2": - version: 1.7.2 - resolution: "@swc/core-linux-x64-musl@npm:1.7.2" +"@swc/core-linux-x64-musl@npm:1.7.5": + version: 1.7.5 + resolution: "@swc/core-linux-x64-musl@npm:1.7.5" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@swc/core-win32-arm64-msvc@npm:1.7.2": - version: 1.7.2 - resolution: "@swc/core-win32-arm64-msvc@npm:1.7.2" +"@swc/core-win32-arm64-msvc@npm:1.7.5": + version: 1.7.5 + resolution: "@swc/core-win32-arm64-msvc@npm:1.7.5" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@swc/core-win32-ia32-msvc@npm:1.7.2": - version: 1.7.2 - resolution: "@swc/core-win32-ia32-msvc@npm:1.7.2" +"@swc/core-win32-ia32-msvc@npm:1.7.5": + version: 1.7.5 + resolution: "@swc/core-win32-ia32-msvc@npm:1.7.5" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@swc/core-win32-x64-msvc@npm:1.7.2": - version: 1.7.2 - resolution: "@swc/core-win32-x64-msvc@npm:1.7.2" +"@swc/core-win32-x64-msvc@npm:1.7.5": + version: 1.7.5 + resolution: "@swc/core-win32-x64-msvc@npm:1.7.5" conditions: os=win32 & cpu=x64 languageName: node linkType: hard -"@swc/core@npm:^1.5.7, @swc/core@npm:^1.7.2": - version: 1.7.2 - resolution: "@swc/core@npm:1.7.2" +"@swc/core@npm:^1.5.7, @swc/core@npm:^1.7.5": + version: 1.7.5 + resolution: "@swc/core@npm:1.7.5" dependencies: - "@swc/core-darwin-arm64": "npm:1.7.2" - "@swc/core-darwin-x64": "npm:1.7.2" - "@swc/core-linux-arm-gnueabihf": "npm:1.7.2" - "@swc/core-linux-arm64-gnu": "npm:1.7.2" - "@swc/core-linux-arm64-musl": "npm:1.7.2" - "@swc/core-linux-x64-gnu": "npm:1.7.2" - "@swc/core-linux-x64-musl": "npm:1.7.2" - "@swc/core-win32-arm64-msvc": "npm:1.7.2" - "@swc/core-win32-ia32-msvc": "npm:1.7.2" - "@swc/core-win32-x64-msvc": "npm:1.7.2" + "@swc/core-darwin-arm64": "npm:1.7.5" + "@swc/core-darwin-x64": "npm:1.7.5" + "@swc/core-linux-arm-gnueabihf": "npm:1.7.5" + "@swc/core-linux-arm64-gnu": "npm:1.7.5" + "@swc/core-linux-arm64-musl": "npm:1.7.5" + "@swc/core-linux-x64-gnu": "npm:1.7.5" + "@swc/core-linux-x64-musl": "npm:1.7.5" + "@swc/core-win32-arm64-msvc": "npm:1.7.5" + "@swc/core-win32-ia32-msvc": "npm:1.7.5" + "@swc/core-win32-x64-msvc": "npm:1.7.5" "@swc/counter": "npm:^0.1.3" "@swc/types": "npm:^0.1.12" peerDependencies: @@ -4091,7 +4091,7 @@ __metadata: peerDependenciesMeta: "@swc/helpers": optional: true - checksum: 10c0/fa07d7fce91a36554b468af9eceba599b94d2ae68a6a3756566bc03a33726f994ba6be419b8f50d543950442e8caa41738d547a9d2ee41d11f65332d6f6f6cb1 + checksum: 10c0/06fcd863348da77dc27dbd66e89bab5f37e391a41afdf044df7ba98639f4b49221fb40c8cd503508ac282ed002d90eb8203abaa02c3f1e4ba04b2f7509628484 languageName: node linkType: hard @@ -4811,12 +4811,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:*, @types/node@npm:^20.14.12": - version: 20.14.12 - resolution: "@types/node@npm:20.14.12" +"@types/node@npm:*, @types/node@npm:^20.14.14": + version: 20.14.14 + resolution: "@types/node@npm:20.14.14" dependencies: undici-types: "npm:~5.26.4" - checksum: 10c0/59bc5fa11fdd23fd517f859063118f54a1ab53d3399ef63c926f8902429d7453abc0db22ef4b0a6110026b6ab81b6472fee894e1d235c24b01a0b3e10cfae0bb + checksum: 10c0/4fc8d368df2b6f5497698327b30db68d7d20e32221ce7d057fb15cbd5834685b2fde0440609e4cb2204e5d305b928f008faf41b950a425f3fd55b60cb1b997cf languageName: node linkType: hard @@ -5053,15 +5053,15 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/eslint-plugin@npm:^7.17.0": - version: 7.17.0 - resolution: "@typescript-eslint/eslint-plugin@npm:7.17.0" +"@typescript-eslint/eslint-plugin@npm:^7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/eslint-plugin@npm:7.18.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:7.17.0" - "@typescript-eslint/type-utils": "npm:7.17.0" - "@typescript-eslint/utils": "npm:7.17.0" - "@typescript-eslint/visitor-keys": "npm:7.17.0" + "@typescript-eslint/scope-manager": "npm:7.18.0" + "@typescript-eslint/type-utils": "npm:7.18.0" + "@typescript-eslint/utils": "npm:7.18.0" + "@typescript-eslint/visitor-keys": "npm:7.18.0" graphemer: "npm:^1.4.0" ignore: "npm:^5.3.1" natural-compare: "npm:^1.4.0" @@ -5072,44 +5072,44 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/654d589531ae45b8ca8f3969e785926b2544100a985968d86c828e2a1ff50331250e19c8b4af83a4ba17847a0047479662eb317e4ad94f6279cac03acd5cda5a + checksum: 10c0/2b37948fa1b0dab77138909dabef242a4d49ab93e4019d4ef930626f0a7d96b03e696cd027fa0087881c20e73be7be77c942606b4a76fa599e6b37f6985304c3 languageName: node linkType: hard -"@typescript-eslint/parser@npm:^7.17.0": - version: 7.17.0 - resolution: "@typescript-eslint/parser@npm:7.17.0" +"@typescript-eslint/parser@npm:^7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/parser@npm:7.18.0" dependencies: - "@typescript-eslint/scope-manager": "npm:7.17.0" - "@typescript-eslint/types": "npm:7.17.0" - "@typescript-eslint/typescript-estree": "npm:7.17.0" - "@typescript-eslint/visitor-keys": "npm:7.17.0" + "@typescript-eslint/scope-manager": "npm:7.18.0" + "@typescript-eslint/types": "npm:7.18.0" + "@typescript-eslint/typescript-estree": "npm:7.18.0" + "@typescript-eslint/visitor-keys": "npm:7.18.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.56.0 peerDependenciesMeta: typescript: optional: true - checksum: 10c0/0cf6922412517b4c005609b035119ddd2798e1b6e74e1bccd487aa53119d27067cfd89311f00b8e96b2b044a0fb7373418a16552be86079879158b260c397418 + checksum: 10c0/370e73fca4278091bc1b657f85e7d74cd52b24257ea20c927a8e17546107ce04fbf313fec99aed0cc2a145ddbae1d3b12e9cc2c1320117636dc1281bcfd08059 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:7.17.0": - version: 7.17.0 - resolution: "@typescript-eslint/scope-manager@npm:7.17.0" +"@typescript-eslint/scope-manager@npm:7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/scope-manager@npm:7.18.0" dependencies: - "@typescript-eslint/types": "npm:7.17.0" - "@typescript-eslint/visitor-keys": "npm:7.17.0" - checksum: 10c0/e1a693e19dc855fe6d04b46c6c205019bfc937eda5f8b255393f8267ebddd282165568336e37b04aab544b155a807784b9c4a92129dfc7c1eef5a9e9fe052685 + "@typescript-eslint/types": "npm:7.18.0" + "@typescript-eslint/visitor-keys": "npm:7.18.0" + checksum: 10c0/038cd58c2271de146b3a594afe2c99290034033326d57ff1f902976022c8b0138ffd3cb893ae439ae41003b5e4bcc00cabf6b244ce40e8668f9412cc96d97b8e languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:7.17.0": - version: 7.17.0 - resolution: "@typescript-eslint/type-utils@npm:7.17.0" +"@typescript-eslint/type-utils@npm:7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/type-utils@npm:7.18.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:7.17.0" - "@typescript-eslint/utils": "npm:7.17.0" + "@typescript-eslint/typescript-estree": "npm:7.18.0" + "@typescript-eslint/utils": "npm:7.18.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^1.3.0" peerDependencies: @@ -5117,23 +5117,23 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/b415cf37c0922cded78735c5049cb5a5b0065e1c0ce4a81ca2a26422763ccacca8945efa45480f40530f2ec414a14d35a88a6798258aa889f7a9cf4ca4a240cd + checksum: 10c0/ad92a38007be620f3f7036f10e234abdc2fdc518787b5a7227e55fd12896dacf56e8b34578723fbf9bea8128df2510ba8eb6739439a3879eda9519476d5783fd languageName: node linkType: hard -"@typescript-eslint/types@npm:7.17.0": - version: 7.17.0 - resolution: "@typescript-eslint/types@npm:7.17.0" - checksum: 10c0/8f734294d432b37c534f17eb2befdfe43b76874d09118d6adf7e308e5a586e9e11b7021abe4f6692a6e6226de58a15b3cfe1300939556ce1c908d9af627b7400 +"@typescript-eslint/types@npm:7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/types@npm:7.18.0" + checksum: 10c0/eb7371ac55ca77db8e59ba0310b41a74523f17e06f485a0ef819491bc3dd8909bb930120ff7d30aaf54e888167e0005aa1337011f3663dc90fb19203ce478054 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:7.17.0": - version: 7.17.0 - resolution: "@typescript-eslint/typescript-estree@npm:7.17.0" +"@typescript-eslint/typescript-estree@npm:7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/typescript-estree@npm:7.18.0" dependencies: - "@typescript-eslint/types": "npm:7.17.0" - "@typescript-eslint/visitor-keys": "npm:7.17.0" + "@typescript-eslint/types": "npm:7.18.0" + "@typescript-eslint/visitor-keys": "npm:7.18.0" debug: "npm:^4.3.4" globby: "npm:^11.1.0" is-glob: "npm:^4.0.3" @@ -5143,31 +5143,31 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 10c0/10967823ce00c9f8cd4a8b56bed3524c098e38cc0e27aaa49ffd8fad4e671c00226bf0330ba858948750b88dc55527ebeb62c74be8a30bac18a106d6c033ab59 + checksum: 10c0/0c7f109a2e460ec8a1524339479cf78ff17814d23c83aa5112c77fb345e87b3642616291908dcddea1e671da63686403dfb712e4a4435104f92abdfddf9aba81 languageName: node linkType: hard -"@typescript-eslint/utils@npm:7.17.0": - version: 7.17.0 - resolution: "@typescript-eslint/utils@npm:7.17.0" +"@typescript-eslint/utils@npm:7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/utils@npm:7.18.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.4.0" - "@typescript-eslint/scope-manager": "npm:7.17.0" - "@typescript-eslint/types": "npm:7.17.0" - "@typescript-eslint/typescript-estree": "npm:7.17.0" + "@typescript-eslint/scope-manager": "npm:7.18.0" + "@typescript-eslint/types": "npm:7.18.0" + "@typescript-eslint/typescript-estree": "npm:7.18.0" peerDependencies: eslint: ^8.56.0 - checksum: 10c0/1f3e22820b3ab3e47809c45e576614ad4a965f5c8634856eca5c70981386b9351a77fb172ba32345e7c5667479cf9526c673699dd38dccd0616ad6db21704e72 + checksum: 10c0/a25a6d50eb45c514469a01ff01f215115a4725fb18401055a847ddf20d1b681409c4027f349033a95c4ff7138d28c3b0a70253dfe8262eb732df4b87c547bd1e languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:7.17.0": - version: 7.17.0 - resolution: "@typescript-eslint/visitor-keys@npm:7.17.0" +"@typescript-eslint/visitor-keys@npm:7.18.0": + version: 7.18.0 + resolution: "@typescript-eslint/visitor-keys@npm:7.18.0" dependencies: - "@typescript-eslint/types": "npm:7.17.0" + "@typescript-eslint/types": "npm:7.18.0" eslint-visitor-keys: "npm:^3.4.3" - checksum: 10c0/fa6b339d51fc3710288bb2ffaa46d639551d77965cc42c36f96c4f43aed663ff12972e8a28652a280f6ce20b7a92dc2aea14b2b4049012799be2fc2d3cbb2c60 + checksum: 10c0/538b645f8ff1d9debf264865c69a317074eaff0255e63d7407046176b0f6a6beba34a6c51d511f12444bae12a98c69891eb6f403c9f54c6c2e2849d1c1cb73c0 languageName: node linkType: hard @@ -6280,9 +6280,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.0, caniuse-lite@npm:^1.0.30001587, caniuse-lite@npm:^1.0.30001599": - version: 1.0.30001643 - resolution: "caniuse-lite@npm:1.0.30001643" - checksum: 10c0/7fcd0fd180bbe6764311ad57b0d39c23afdcc3bb1d8f804e7a76752c62a85b1bb7cf74b672d9da2f0afe7ad75336ff811a6fe279eb2a54bc04c272b6b62e57f1 + version: 1.0.30001646 + resolution: "caniuse-lite@npm:1.0.30001646" + checksum: 10c0/ecdd87c08cd63fa32e11311dfa3543a52613b0b99498b6fe6f2c66af65cc27e2f7436fa5b2bc2bcf72174448a7670715b284d420de838bcf3e811864371a2465 languageName: node linkType: hard From 17ab3a831f2a593fdf03e6bff0e9abb25cc8ee81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Fri, 2 Aug 2024 22:15:33 +0200 Subject: [PATCH 09/10] refactor(semantics): remove shadow predicate interpretations during generation While shadow predicate interpretations are useful for debugging model semantics, may match sets for some predicates can grow very large during model generation. Do not consturct interpretations for shadow predicates in ModelGeneratorFactory by default. Also fixes a bug where the candidate interpretation of a shadow predicate was forced to be a refinement of the partial interpretation. --- .../generator/ModelFacadeFactory.java | 20 ++++-- .../generator/ModelGeneratorFactory.java | 6 +- .../generator/ModelSemanticsFactory.java | 2 +- .../generator/FileBasedSemanticsTest.java | 9 +-- .../language/semantics/ModelInitializer.java | 59 +++++++++------ .../language/semantics/ProblemTraceImpl.java | 13 ++++ .../predicate/ShadowPredicateTranslator.java | 71 +++++++++++++++++++ 7 files changed, 150 insertions(+), 30 deletions(-) create mode 100644 subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/ShadowPredicateTranslator.java diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/ModelFacadeFactory.java b/subprojects/generator/src/main/java/tools/refinery/generator/ModelFacadeFactory.java index 75ca5082d..173be38e4 100644 --- a/subprojects/generator/src/main/java/tools/refinery/generator/ModelFacadeFactory.java +++ b/subprojects/generator/src/main/java/tools/refinery/generator/ModelFacadeFactory.java @@ -10,6 +10,8 @@ import tools.refinery.language.semantics.ModelInitializer; import tools.refinery.store.util.CancellationToken; +// This class is used as a fluent builder, so it's not necessary to use the return value of all of its methods. +@SuppressWarnings("UnusedReturnValue") public abstract sealed class ModelFacadeFactory> permits ModelSemanticsFactory, ModelGeneratorFactory { @Inject @@ -17,7 +19,9 @@ public abstract sealed class ModelFacadeFactory> private CancellationToken cancellationToken = CancellationToken.NONE; - private boolean keepNonExistingObjects = false; + private boolean keepNonExistingObjects; + + private boolean keepShadowPredicates = true; protected abstract T getSelf(); @@ -26,13 +30,21 @@ public T cancellationToken(CancellationToken cancellationToken) { return getSelf(); } - public T keepNonExistingObjects(boolean removeNonExistentObjects) { - this.keepNonExistingObjects = removeNonExistentObjects; + public T keepNonExistingObjects(boolean keepNonExistentObjects) { + this.keepNonExistingObjects = keepNonExistentObjects; + return getSelf(); + } + + public T keepShadowPredicates(boolean keepShadowPredicates) { + this.keepShadowPredicates = keepShadowPredicates; return getSelf(); } protected ModelInitializer createModelInitializer() { - return initializerProvider.get(); + var initializer = initializerProvider.get(); + initializer.setKeepNonExistingObjects(keepNonExistingObjects); + initializer.setKeepShadowPredicates(keepShadowPredicates); + return initializer; } protected CancellationToken getCancellationToken() { diff --git a/subprojects/generator/src/main/java/tools/refinery/generator/ModelGeneratorFactory.java b/subprojects/generator/src/main/java/tools/refinery/generator/ModelGeneratorFactory.java index b041c8f6b..3205eca29 100644 --- a/subprojects/generator/src/main/java/tools/refinery/generator/ModelGeneratorFactory.java +++ b/subprojects/generator/src/main/java/tools/refinery/generator/ModelGeneratorFactory.java @@ -35,6 +35,10 @@ public final class ModelGeneratorFactory extends ModelFacadeFactory testWithNonExistingObjects() { - return getFileBasedTests(true); + return getFileBasedTests(true, true); } @TestFactory Stream testWithoutNonExistingObjects() { - return getFileBasedTests(false); + return getFileBasedTests(false, false); } - private Stream getFileBasedTests(boolean keepNonExistingObjects) { + private Stream getFileBasedTests(boolean keepNonExistingObjects, boolean keepShadowPredicates) { loader.setSemanticsFactoryProvider(() -> semanticsFactoryProvider.get() - .keepNonExistingObjects(keepNonExistingObjects)); + .keepNonExistingObjects(keepNonExistingObjects) + .keepShadowPredicates(keepShadowPredicates)); return loader.allFromClasspath(getClass()); } } diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java index 3defc0c0f..f45acc960 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ModelInitializer.java @@ -41,6 +41,7 @@ import tools.refinery.store.reasoning.translator.multiplicity.UnconstrainedMultiplicity; import tools.refinery.store.reasoning.translator.predicate.BasePredicateTranslator; import tools.refinery.store.reasoning.translator.predicate.PredicateTranslator; +import tools.refinery.store.reasoning.translator.predicate.ShadowPredicateTranslator; import tools.refinery.store.statecoding.StateCoderBuilder; import tools.refinery.store.tuple.Tuple; import tools.refinery.store.tuple.Tuple1; @@ -69,6 +70,10 @@ public class ModelInitializer { @Inject private RuleCompiler ruleCompiler; + private boolean keepNonExistingObjects; + + private boolean keepShadowPredicates = true; + private Problem problem; private final Set importedProblems = new HashSet<>(); @@ -181,10 +186,6 @@ private void loadImportedProblems() { } public void configureStoreBuilder(ModelStoreBuilder storeBuilder) { - configureStoreBuilder(storeBuilder, false); - } - - public void configureStoreBuilder(ModelStoreBuilder storeBuilder, boolean keepNonExistingObjects) { checkProblem(); try { storeBuilder.with(new MultiObjectTranslator(keepNonExistingObjects)); @@ -199,6 +200,9 @@ public void configureStoreBuilder(ModelStoreBuilder storeBuilder, boolean keepNo collectRules(storeBuilder); storeBuilder.tryGetAdapter(StateCoderBuilder.class) .ifPresent(stateCoderBuilder -> stateCoderBuilder.individuals(individuals)); + if (!keepShadowPredicates) { + problemTrace.removeShadowRelations(); + } } catch (TranslationException e) { throw problemTrace.wrapException(e); } @@ -599,6 +603,8 @@ private void collectPredicateDefinitionTraced(PredicateDefinition predicateDefin private void collectPredicateDefinition(PredicateDefinition predicateDefinition, ModelStoreBuilder storeBuilder) { if (ProblemUtil.isBasePredicate(predicateDefinition)) { collectBasePredicateDefinition(predicateDefinition, storeBuilder); + } else if (predicateDefinition.getKind() == PredicateKind.SHADOW) { + collectShadowPredicateDefinition(predicateDefinition, storeBuilder); } else { collectComputedPredicateDefinition(predicateDefinition, storeBuilder); } @@ -608,25 +614,18 @@ private void collectComputedPredicateDefinition(PredicateDefinition predicateDef ModelStoreBuilder storeBuilder) { var partialRelation = getPartialRelation(predicateDefinition); var query = queryCompiler.toQuery(partialRelation.name(), predicateDefinition); - List parameterTypes = null; - boolean mutable; + boolean mutable = targetTypes.contains(partialRelation) || isActionTarget(predicateDefinition); TruthValue defaultValue; - if (predicateDefinition.getKind() == PredicateKind.SHADOW) { - mutable = false; - defaultValue = TruthValue.UNKNOWN; + if (predicateDefinition.getKind() == PredicateKind.ERROR) { + defaultValue = TruthValue.FALSE; } else { - mutable = targetTypes.contains(partialRelation) || isActionTarget(predicateDefinition); - if (predicateDefinition.getKind() == PredicateKind.ERROR) { - defaultValue = TruthValue.FALSE; - } else { - var seed = modelSeed.getSeed(partialRelation); - defaultValue = seed.majorityValue() == TruthValue.FALSE ? TruthValue.FALSE : TruthValue.UNKNOWN; - var cursor = seed.getCursor(defaultValue, problemTrace.getNodeTrace().size()); - // The symbol should be mutable if there is at least one non-default entry in the seed. - mutable = mutable || cursor.move(); - } - parameterTypes = getParameterTypes(predicateDefinition, null); + var seed = modelSeed.getSeed(partialRelation); + defaultValue = seed.majorityValue() == TruthValue.FALSE ? TruthValue.FALSE : TruthValue.UNKNOWN; + var cursor = seed.getCursor(defaultValue, problemTrace.getNodeTrace().size()); + // The symbol should be mutable if there is at least one non-default entry in the seed. + mutable = mutable || cursor.move(); } + var parameterTypes = getParameterTypes(predicateDefinition, null); var translator = new PredicateTranslator(partialRelation, query, parameterTypes, mutable, defaultValue); storeBuilder.with(translator); } @@ -662,6 +661,14 @@ private void collectBasePredicateDefinition(PredicateDefinition predicateDefinit storeBuilder.with(translator); } + private void collectShadowPredicateDefinition(PredicateDefinition predicateDefinition, + ModelStoreBuilder storeBuilder) { + var partialRelation = getPartialRelation(predicateDefinition); + var query = queryCompiler.toQuery(partialRelation.name(), predicateDefinition); + var translator = new ShadowPredicateTranslator(partialRelation, query, keepShadowPredicates); + storeBuilder.with(translator); + } + private void collectScopes() { for (var importedProblem : importedProblems) { for (var statement : importedProblem.getStatements()) { @@ -710,6 +717,18 @@ private void collectTypeScope(TypeScope typeScope) { scopePropagator.scope(type, interval); } + public void setKeepNonExistingObjects(boolean keepNonExistingObjects) { + this.keepNonExistingObjects = keepNonExistingObjects; + } + + public boolean isKeepShadowPredicates() { + return keepShadowPredicates; + } + + public void setKeepShadowPredicates(boolean keepShadowPredicates) { + this.keepShadowPredicates = keepShadowPredicates; + } + private record RelationInfo(PartialRelation partialRelation, MutableSeed assertions, MutableSeed defaultAssertions) { public RelationInfo(String name, int arity, TruthValue value, TruthValue defaultValue) { diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java index 457f2362e..487ac9b9c 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/ProblemTraceImpl.java @@ -113,6 +113,19 @@ void putRelation(Relation relation, PartialRelation partialRelation) { } } + void removeShadowRelations() { + var iterator = mutableRelationTrace.entrySet().iterator(); + while (iterator.hasNext()) { + var entry = iterator.next(); + var relation = entry.getKey(); + if (relation instanceof PredicateDefinition predicateDefinition && + predicateDefinition.getKind() == PredicateKind.SHADOW) { + iterator.remove(); + mutableInverseTrace.remove(entry.getValue()); + } + } + } + @Override public Map getInverseRelationTrace() { return inverseTrace; diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/ShadowPredicateTranslator.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/ShadowPredicateTranslator.java new file mode 100644 index 000000000..08cc92359 --- /dev/null +++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/predicate/ShadowPredicateTranslator.java @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2024 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.translator.predicate; + +import tools.refinery.logic.dnf.RelationalQuery; +import tools.refinery.logic.term.truthvalue.TruthValue; +import tools.refinery.store.map.Cursor; +import tools.refinery.store.model.ModelStoreBuilder; +import tools.refinery.store.model.ModelStoreConfiguration; +import tools.refinery.store.reasoning.ReasoningAdapter; +import tools.refinery.store.reasoning.ReasoningBuilder; +import tools.refinery.store.reasoning.interpretation.AbstractPartialInterpretation; +import tools.refinery.store.reasoning.interpretation.QueryBasedComputedRewriter; +import tools.refinery.store.reasoning.literal.Concreteness; +import tools.refinery.store.reasoning.literal.Modality; +import tools.refinery.store.reasoning.representation.PartialRelation; +import tools.refinery.store.reasoning.representation.PartialSymbol; +import tools.refinery.store.reasoning.translator.PartialRelationTranslator; +import tools.refinery.store.tuple.Tuple; + +public class ShadowPredicateTranslator implements ModelStoreConfiguration { + private final PartialRelation relation; + private final RelationalQuery query; + private final boolean hasInterpretation; + + public ShadowPredicateTranslator(PartialRelation relation, RelationalQuery query, boolean hasInterpretation) { + this.relation = relation; + this.query = query; + this.hasInterpretation = hasInterpretation; + } + + @Override + public void apply(ModelStoreBuilder storeBuilder) { + var reasoningBuilder = storeBuilder.getAdapter(ReasoningBuilder.class); + var may = reasoningBuilder.lift(Modality.MAY, Concreteness.PARTIAL, query); + var must = reasoningBuilder.lift(Modality.MAY, Concreteness.PARTIAL, query); + // Do not let {@link PartialRelationTranslator} merge the partial queries into the candidate ones. + var candidateMay = reasoningBuilder.lift(Modality.MAY, Concreteness.PARTIAL, query); + var candidateMust = reasoningBuilder.lift(Modality.MAY, Concreteness.PARTIAL, query); + var translator = PartialRelationTranslator.of(relation) + .rewriter(new QueryBasedComputedRewriter(may, must, candidateMay, candidateMust, query)); + if (!hasInterpretation) { + translator.interpretation(MissingInterpretation::new); + } + storeBuilder.with(translator); + } + + private static class MissingInterpretation extends AbstractPartialInterpretation { + public MissingInterpretation(ReasoningAdapter adapter, Concreteness concreteness, + PartialSymbol partialSymbol) { + super(adapter, concreteness, partialSymbol); + } + + @Override + public TruthValue get(Tuple key) { + return fail(); + } + + @Override + public Cursor getAll() { + return fail(); + } + + private T fail() { + throw new UnsupportedOperationException("No interpretation for shadow predicate: " + getPartialSymbol()); + } + } +} From 0fe212036509e0255dfd747c1060ca5a76826bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krist=C3=B3f=20Marussy?= Date: Fri, 2 Aug 2024 22:36:46 +0200 Subject: [PATCH 10/10] feat(semantics): base predicate serialization --- .../semantics/SolutionSerializer.java | 23 ++++++-- .../semantics/SolutionSerializerTest.java | 59 +++++++++++++++++++ .../refinery/language/utils/ProblemUtil.java | 4 ++ 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java index ed4841c48..16a41824c 100644 --- a/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java +++ b/subprojects/language-semantics/src/main/java/tools/refinery/language/semantics/SolutionSerializer.java @@ -24,6 +24,7 @@ import tools.refinery.language.scoping.imports.ImportAdapterProvider; import tools.refinery.language.typesystem.SignatureProvider; import tools.refinery.language.utils.ProblemUtil; +import tools.refinery.logic.term.truthvalue.TruthValue; import tools.refinery.store.model.Model; import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.interpretation.PartialInterpretation; @@ -31,13 +32,15 @@ import tools.refinery.store.reasoning.representation.PartialRelation; import tools.refinery.store.reasoning.translator.typehierarchy.InferredType; import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchyTranslator; -import tools.refinery.logic.term.truthvalue.TruthValue; import tools.refinery.store.tuple.Tuple; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; -import java.util.*; +import java.util.Map; +import java.util.Objects; +import java.util.TreeMap; +import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; @@ -107,6 +110,7 @@ public Problem serializeSolution(ProblemTrace trace, Model model, URI uri) { addExistsAssertions(); addClassAssertions(); addReferenceAssertions(); + addBasePredicateAssertions(); if (nodeDeclaration.getNodes().isEmpty()) { problem.getStatements().remove(nodeDeclaration); } @@ -249,8 +253,8 @@ private void addExistsAssertions() { } private void addClassAssertions() { - var types = - trace.getMetamodel().typeHierarchy().getPreservedTypes().keySet().stream().collect(Collectors.toMap(Function.identity(), this::findPartialRelation)); + var types = trace.getMetamodel().typeHierarchy().getPreservedTypes().keySet().stream() + .collect(Collectors.toMap(Function.identity(), this::findPartialRelation)); var cursor = model.getInterpretation(TypeHierarchyTranslator.TYPE_SYMBOL).getAll(); while (cursor.move()) { var key = cursor.getKey(); @@ -306,6 +310,17 @@ private void addReferenceAssertions() { } } + private void addBasePredicateAssertions() { + for (var entry : trace.getRelationTrace().entrySet()) { + if (entry.getKey() instanceof PredicateDefinition predicateDefinition && + ProblemUtil.isBasePredicate(predicateDefinition)) { + var partialRelation = entry.getValue(); + addDefaultAssertion(partialRelation); + addAssertions(partialRelation); + } + } + } + private void addAssertions(PartialRelation partialRelation) { var relation = findPartialRelation(partialRelation); var cursor = reasoningAdapter.getPartialInterpretation(Concreteness.CANDIDATE, partialRelation).getAll(); diff --git a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java index 69b5f1c2a..fded04bf8 100644 --- a/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java +++ b/subprojects/language-semantics/src/test/java/tools/refinery/language/semantics/SolutionSerializerTest.java @@ -234,6 +234,65 @@ class Foo. !exists(a). !exists(Foo::new). Foo(foo1). + """), Arguments.of(""" + class Foo { + partial Bar[] bar + } + + class Bar. + """, """ + bar(a, b). + scope Foo = 2, Bar = 2. + """, """ + declare a, b, foo1, bar1. + !exists(Foo::new). + !exists(Bar::new). + Foo(foo1). + Bar(bar1). + Foo(a). + Bar(b). + default !bar(*, *). + ?bar(foo1, bar1). + ?bar(foo1, b). + ?bar(a, bar1). + bar(a, b). + """), Arguments.of(""" + class Foo. + class Bar. + pred bar(Foo x, Bar y). + """, """ + bar(a, b). + scope Foo = 2, Bar = 2. + """, """ + declare a, b, foo1, bar1. + !exists(Foo::new). + !exists(Bar::new). + Foo(foo1). + Bar(bar1). + Foo(a). + Bar(b). + default !bar(*, *). + bar(a, b). + """), Arguments.of(""" + class Foo. + class Bar. + partial pred bar(Foo x, Bar y). + """, """ + bar(a, b). + scope Foo = 2, Bar = 2. + """, """ + declare a, b, foo1, bar1. + !exists(Foo::new). + !exists(Bar::new). + Foo(foo1). + Bar(bar1). + Foo(a). + Bar(b). + default !bar(*, *). + ?bar(foo1, bar1). + ?bar(foo1, b). + ?bar(a, bar1). + bar(a, b). """)); } } diff --git a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java index 55b0ca604..75e2ded01 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java +++ b/subprojects/language/src/main/java/tools/refinery/language/utils/ProblemUtil.java @@ -121,6 +121,10 @@ public static boolean isDerivedStatePredicate(PredicateDefinition predicateDefin } public static boolean isBasePredicate(PredicateDefinition predicateDefinition) { + if (isBuiltIn(predicateDefinition) || predicateDefinition == null) { + // Built-in predicates have no clauses, but are not base. + return false; + } return switch (predicateDefinition.getKind()) { case DEFAULT -> predicateDefinition.getBodies().isEmpty(); case PARTIAL -> true;