/*
* SPDX-FileCopyrightText: 2023 The Refinery Authors
*
* SPDX-License-Identifier: EPL-2.0
*/
package tools.refinery.store.reasoning.scope;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import tools.refinery.store.dse.propagation.PropagationAdapter;
import tools.refinery.store.dse.strategy.BestFirstStoreManager;
import tools.refinery.store.dse.transition.DesignSpaceExplorationAdapter;
import tools.refinery.store.model.ModelStore;
import tools.refinery.store.query.dnf.Query;
import tools.refinery.store.query.interpreter.QueryInterpreterAdapter;
import tools.refinery.store.query.term.Variable;
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;
import tools.refinery.store.reasoning.representation.PartialRelation;
import tools.refinery.store.reasoning.seed.ModelSeed;
import tools.refinery.store.reasoning.translator.PartialRelationTranslator;
import tools.refinery.store.reasoning.translator.containment.ContainmentHierarchyTranslator;
import tools.refinery.store.reasoning.translator.metamodel.Metamodel;
import tools.refinery.store.reasoning.translator.metamodel.MetamodelTranslator;
import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator;
import tools.refinery.store.representation.TruthValue;
import tools.refinery.store.representation.cardinality.CardinalityIntervals;
import tools.refinery.store.statecoding.StateCoderAdapter;
import tools.refinery.store.tuple.Tuple;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static tools.refinery.store.query.literal.Literals.not;
class PredicateScopeTest {
private static final PartialRelation index = new PartialRelation("Index", 1);
private static final PartialRelation next = new PartialRelation("next", 2);
private static final PartialRelation nextInvalidMultiplicity = new PartialRelation("next::invalidMultiplicity", 1);
private static final PartialRelation prev = new PartialRelation("prev", 2);
private static final PartialRelation prevInvalidMultiplicity = new PartialRelation("prev::invalidMultiplicity", 1);
private static final PartialRelation loop = new PartialRelation("loop", 1);
private static final PartialRelation first = new PartialRelation("first", 1);
private static final PartialRelation last = new PartialRelation("last", 1);
@Tag("slow")
@ParameterizedTest
@ValueSource(ints = {1, 2, 3, 4, 5})
void generateTest(int randomSeed) {
var store = createStore();
var newIndex = Tuple.of(0);
var modelSeed = ModelSeed.builder(1)
.seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder
.reducedValue(CardinalityIntervals.ONE)
.put(newIndex, CardinalityIntervals.SET))
.seed(ContainmentHierarchyTranslator.CONTAINED_SYMBOL,
builder -> builder.reducedValue(TruthValue.UNKNOWN))
.seed(index, builder -> builder
.reducedValue(TruthValue.UNKNOWN)
.put(newIndex, TruthValue.TRUE))
.seed(next, builder -> builder.reducedValue(TruthValue.UNKNOWN))
.seed(prev, builder -> builder.reducedValue(TruthValue.UNKNOWN))
.build();
var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed);
var initialVersion = model.commit();
var bestFistSearch = new BestFirstStoreManager(store, 1);
bestFistSearch.startExploration(initialVersion, randomSeed);
model.restore(bestFistSearch.getSolutionStore().getSolutions().get(0).version());
var reasoningAdapter = model.getAdapter(ReasoningAdapter.class);
var firstInterpretation = reasoningAdapter.getPartialInterpretation(Concreteness.CANDIDATE, first);
assertSize(firstInterpretation, 1);
var lastInterpretation = reasoningAdapter.getPartialInterpretation(Concreteness.CANDIDATE, last);
assertSize(lastInterpretation, 1);
}
@Test
void invalidResultTest() {
var store = createStore();
var modelSeed = ModelSeed.builder(8)
.seed(MultiObjectTranslator.COUNT_SYMBOL, builder -> builder.reducedValue(CardinalityIntervals.ONE))
.seed(ContainmentHierarchyTranslator.CONTAINED_SYMBOL,
builder -> builder.reducedValue(TruthValue.UNKNOWN))
.seed(ContainmentHierarchyTranslator.CONTAINS_SYMBOL,
builder -> builder.reducedValue(TruthValue.UNKNOWN))
.seed(index, builder -> builder.reducedValue(TruthValue.TRUE))
.seed(next, builder -> builder
.reducedValue(TruthValue.UNKNOWN)
.put(Tuple.of(1, 2), TruthValue.TRUE)
.put(Tuple.of(2, 3), TruthValue.TRUE)
.put(Tuple.of(3, 4), TruthValue.TRUE)
.put(Tuple.of(4, 5), TruthValue.TRUE)
.put(Tuple.of(6, 1), TruthValue.TRUE))
.seed(prev, builder -> builder.reducedValue(TruthValue.UNKNOWN))
.build();
var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed);
var reasoningAdapter = model.getAdapter(ReasoningAdapter.class);
var firstInterpretation = reasoningAdapter.getPartialInterpretation(Concreteness.CANDIDATE, first);
assertSize(firstInterpretation, 3);
var lastInterpretation = reasoningAdapter.getPartialInterpretation(Concreteness.CANDIDATE, last);
assertSize(lastInterpretation, 3);
var designSpaceAdapter = model.getAdapter(DesignSpaceExplorationAdapter.class);
assertThat(designSpaceAdapter.checkAccept(), is(false));
}
private ModelStore createStore() {
var metamodel = Metamodel.builder()
.type(index)
.reference(next, builder -> builder
.source(index)
.target(index)
.multiplicity(CardinalityIntervals.LONE, nextInvalidMultiplicity)
.opposite(prev))
.reference(prev, builder -> builder
.source(index)
.target(index)
.multiplicity(CardinalityIntervals.LONE, prevInvalidMultiplicity)
.opposite(next))
.build();
return ModelStore.builder()
.with(QueryInterpreterAdapter.builder())
.with(StateCoderAdapter.builder())
.with(PropagationAdapter.builder())
.with(DesignSpaceExplorationAdapter.builder())
.with(ReasoningAdapter.builder())
.with(new MultiObjectTranslator())
.with(new MetamodelTranslator(metamodel))
.with(PartialRelationTranslator.of(loop)
.query(Query.of("loop", (builder, p1) -> builder.clause(
index.call(p1),
next.callTransitive(p1, p1)
)))
.mayNever())
.with(PartialRelationTranslator.of(first)
.query(Query.of("first", (builder, p1) -> builder.clause(
index.call(p1),
not(prev.call(p1, Variable.of()))
))))
.with(PartialRelationTranslator.of(last)
.query(Query.of("last", (builder, p1) -> builder.clause(
index.call(p1),
not(next.call(p1, Variable.of()))
))))
.with(new ScopePropagator()
.scope(index, CardinalityIntervals.exactly(8))
.scope(first, CardinalityIntervals.ONE)
.scope(last, CardinalityIntervals.ONE))
.build();
}
private void assertSize(PartialInterpretation partialInterpretation, int expected) {
int size = 0;
var cursor = partialInterpretation.getAll();
while (cursor.move()) {
assertThat(cursor.getValue(), is(TruthValue.TRUE));
size++;
}
assertThat(size, is(expected));
}
}