/* * SPDX-FileCopyrightText: 2023 The Refinery Authors * * SPDX-License-Identifier: EPL-2.0 */ package tools.refinery.store.reasoning.interpretation; import tools.refinery.store.map.Cursor; import tools.refinery.store.model.ModelStoreBuilder; import tools.refinery.store.query.ModelQueryAdapter; import tools.refinery.store.query.ModelQueryBuilder; import tools.refinery.logic.dnf.Query; import tools.refinery.store.query.resultset.ResultSet; import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.literal.Concreteness; import tools.refinery.store.reasoning.representation.PartialSymbol; import tools.refinery.logic.term.truthvalue.TruthValue; import tools.refinery.store.tuple.Tuple; import java.util.Set; public class QueryBasedRelationInterpretationFactory implements PartialInterpretation.Factory { private final Query may; private final Query must; private final Query candidateMay; private final Query candidateMust; public QueryBasedRelationInterpretationFactory( Query may, Query must, Query candidateMay, Query candidateMust) { this.may = may; this.must = must; this.candidateMay = candidateMay; this.candidateMust = candidateMust; } @Override public PartialInterpretation create( ReasoningAdapter adapter, Concreteness concreteness, PartialSymbol partialSymbol) { var queryEngine = adapter.getModel().getAdapter(ModelQueryAdapter.class); ResultSet mayResultSet; ResultSet mustResultSet; switch (concreteness) { case PARTIAL -> { mayResultSet = queryEngine.getResultSet(may); mustResultSet = queryEngine.getResultSet(must); } case CANDIDATE -> { mayResultSet = queryEngine.getResultSet(candidateMay); mustResultSet = queryEngine.getResultSet(candidateMust); } default -> throw new IllegalArgumentException("Unknown concreteness: " + concreteness); } if (mayResultSet.equals(mustResultSet)) { return new TwoValuedInterpretation(adapter, concreteness, partialSymbol, mustResultSet); } else { return new FourValuedInterpretation( adapter, concreteness, partialSymbol, mayResultSet, mustResultSet); } } @Override public void configure(ModelStoreBuilder storeBuilder, Set requiredInterpretations) { var queryBuilder = storeBuilder.getAdapter(ModelQueryBuilder.class); if (requiredInterpretations.contains(Concreteness.PARTIAL)) { queryBuilder.queries(may, must); } if (requiredInterpretations.contains(Concreteness.CANDIDATE)) { queryBuilder.queries(candidateMay, candidateMust); } } private static class TwoValuedInterpretation extends AbstractPartialInterpretation { private final ResultSet resultSet; protected TwoValuedInterpretation( ReasoningAdapter adapter, Concreteness concreteness, PartialSymbol partialSymbol, ResultSet resultSet) { super(adapter, concreteness, partialSymbol); this.resultSet = resultSet; } @Override public TruthValue get(Tuple key) { return TruthValue.toTruthValue(resultSet.get(key)); } @Override public Cursor getAll() { return new TwoValuedCursor(resultSet.getAll()); } private record TwoValuedCursor(Cursor cursor) implements Cursor { @Override public Tuple getKey() { return cursor.getKey(); } @Override public TruthValue getValue() { return TruthValue.toTruthValue(cursor.getValue()); } @Override public boolean isTerminated() { return cursor.isTerminated(); } @Override public boolean move() { return cursor.move(); } } } private static class FourValuedInterpretation extends AbstractPartialInterpretation { private final ResultSet mayResultSet; private final ResultSet mustResultSet; public FourValuedInterpretation( ReasoningAdapter adapter, Concreteness concreteness, PartialSymbol partialSymbol, ResultSet mayResultSet, ResultSet mustResultSet) { super(adapter, concreteness, partialSymbol); this.mayResultSet = mayResultSet; this.mustResultSet = mustResultSet; } @Override public TruthValue get(Tuple key) { boolean isMay = mayResultSet.get(key); boolean isMust = mustResultSet.get(key); if (isMust) { return isMay ? TruthValue.TRUE : TruthValue.ERROR; } else { return isMay ? TruthValue.UNKNOWN : TruthValue.FALSE; } } @Override public Cursor getAll() { return new FourValuedCursor(); } private final class FourValuedCursor implements Cursor { private final Cursor mayCursor; private Cursor mustCursor; private FourValuedCursor() { this.mayCursor = mayResultSet.getAll(); } @Override public Tuple getKey() { return mustCursor == null ? mayCursor.getKey() : mustCursor.getKey(); } @Override public TruthValue getValue() { if (mustCursor != null) { return TruthValue.ERROR; } if (Boolean.TRUE.equals(mustResultSet.get(mayCursor.getKey()))) { return TruthValue.TRUE; } return TruthValue.UNKNOWN; } @Override public boolean isTerminated() { return mustCursor != null && mustCursor.isTerminated(); } @Override public boolean move() { if (mayCursor.isTerminated()) { return moveMust(); } if (mayCursor.move()) { return true; } mustCursor = mustResultSet.getAll(); return moveMust(); } private boolean moveMust() { while (mustCursor.move()) { // We already iterated over {@code TRUE} truth values with {@code mayCursor}. if (!Boolean.TRUE.equals(mayResultSet.get(mustCursor.getKey()))) { return true; } } return false; } } } }