/* * SPDX-FileCopyrightText: 2023 The Refinery Authors * * SPDX-License-Identifier: EPL-2.0 */ package tools.refinery.store.reasoning.lifting; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import tools.refinery.store.query.dnf.Dnf; import tools.refinery.store.query.dnf.Query; import tools.refinery.store.query.term.ParameterDirection; import tools.refinery.store.query.view.AnySymbolView; import tools.refinery.store.query.view.FunctionView; import tools.refinery.store.query.view.MustView; import tools.refinery.store.reasoning.ReasoningAdapter; import tools.refinery.store.reasoning.literal.Concreteness; import tools.refinery.store.reasoning.literal.ModalConstraint; 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.representation.Symbol; import tools.refinery.store.representation.TruthValue; import java.util.List; import static org.hamcrest.MatcherAssert.assertThat; import static tools.refinery.store.query.literal.Literals.check; import static tools.refinery.store.query.literal.Literals.not; import static tools.refinery.store.query.term.int_.IntTerms.*; import static tools.refinery.store.query.tests.QueryMatchers.structurallyEqualTo; class DnfLifterTest { private static final Symbol friendSymbol = Symbol.of("friend", 2, TruthValue.class, TruthValue.UNKNOWN); private static final AnySymbolView friendMustView = new MustView(friendSymbol); private static final Symbol age = Symbol.of("age", 1, Integer.class); private static final FunctionView ageView = new FunctionView<>(age); private static final PartialRelation person = PartialSymbol.of("Person", 1); private static final PartialRelation friend = PartialSymbol.of("friend", 2); private DnfLifter sut; @BeforeEach void beforeEach() { sut = new DnfLifter(); } @Test void liftPartialRelationCallTest() { var input = Query.of("Actual", (builder, p1) -> builder.clause((v1) -> List.of( friend.call(p1, v1) ))).getDnf(); var actual = sut.lift(Modality.MUST, Concreteness.PARTIAL, input); var expected = Query.of("Expected", (builder, p1) -> builder.clause((v1) -> List.of( ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, friend).call(p1, v1), ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(v1) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftPartialDnfCallTest() { var called = Query.of("Called", (builder, p1, p2) -> builder.clause( friend.call(p1, p2), friend.call(p2, p1) )); var input = Query.of("Actual", (builder, p1) -> builder.clause((v1) -> List.of( called.call(p1, v1) ))).getDnf(); var actual = sut.lift(Modality.MUST, Concreteness.PARTIAL, input); var expected = Query.of("Expected", (builder, p1) -> builder.clause((v1) -> List.of( ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, called.getDnf()).call(p1, v1), ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(v1) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftSymbolViewCallTest() { var input = Query.of("Actual", (builder, p1) -> builder.clause((v1) -> List.of( friendMustView.call(p1, v1) ))).getDnf(); var actual = sut.lift(Modality.MUST, Concreteness.PARTIAL, input); var expected = Query.of("Expected", (builder, p1) -> builder.clause((v1) -> List.of( friendMustView.call(p1, v1), ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(v1) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftPartialRelationNegativeCallTest() { var input = Query.of("Actual", (builder, p1) -> builder.clause((v1) -> List.of( not(friend.call(p1, v1)), friend.call(v1, p1) ))).getDnf(); var actual = sut.lift(Modality.MUST, Concreteness.PARTIAL, input); var expected = Query.of("Expected", (builder, p1) -> builder.clause((v1) -> List.of( not(ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, friend).call(p1, v1)), ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, friend).call(v1, p1), ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(v1) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftPartialRelationQuantifiedNegativeCallTest() { var input = Query.of("Actual", (builder, p1) -> builder.clause((v1) -> List.of( person.call(p1), not(friend.call(p1, v1)) ))).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var helper = Query.of("Helper", (builder, p1, p2) -> builder.clause( ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, friend).call(p1, p2), ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(p2) )); var expected = Query.of("Expected", (builder, p1) -> builder.clause((v1) -> List.of( ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, person).call(p1), not(helper.call(p1, v1)) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftSymbolViewQuantifiedNegativeCallTest() { var input = Query.of("Actual", (builder, p1) -> builder.clause((v1) -> List.of( person.call(p1), not(friendMustView.call(p1, v1)) ))).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var helper = Query.of("Helper", (builder, p1, p2) -> builder.clause( friendMustView.call(p1, p2), ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(p2) )); var expected = Query.of("Expected", (builder, p1) -> builder.clause((v1) -> List.of( ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, person).call(p1), not(helper.call(p1, v1)) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftPartialRelationQuantifiedNegativeDiagonalCallTest() { var input = Query.of("Actual", (builder) -> builder.clause((v1) -> List.of( not(friend.call(v1, v1)) ))).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var helper = Query.of("Helper", (builder, p1) -> builder.clause( ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, friend).call(p1, p1), ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(p1) )); var expected = Query.of("Expected", (builder) -> builder.clause((v1) -> List.of( not(helper.call(v1)) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftPartialDnfQuantifiedNegativeInputCallTest() { var called = Dnf.of("Called", builder -> { var p1 = builder.parameter("p1", ParameterDirection.IN); var p2 = builder.parameter("p2", ParameterDirection.OUT); builder.clause( friend.call(p1, p2), friend.call(p2, p1) ); }); var input = Query.of("Actual", (builder, p1) -> builder.clause((v1) -> List.of( person.call(p1), not(called.call(p1, v1)) ))).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var helper = Dnf.of("Helper", builder -> { var p1 = builder.parameter("p1", ParameterDirection.IN); var p2 = builder.parameter("p2", ParameterDirection.OUT); builder.clause( ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, called).call(p1, p2), ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(p2) ); }); var expected = Query.of("Expected", (builder, p1) -> builder.clause((v1) -> List.of( ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, person).call(p1), not(helper.call(p1, v1)) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftPartialRelationTransitiveCallTest() { var input = Query.of("Actual", (builder, p1, p2)-> builder.clause( friend.callTransitive(p1, p2), not(person.call(p2)) )).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var helper = Query.of("Helper", (builder, p1, p2) -> builder.clause( ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, friend).call(p1, p2), ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(p2) )); var helper2 = Query.of("Helper2", (builder, p1, p2) -> { builder.clause( ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, friend).call(p1, p2) ); builder.clause((v1) -> List.of( helper.callTransitive(p1, v1), ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, friend).call(v1, p2) )); }); var expected = Query.of("Expected", (builder, p1, p2) -> builder.clause( helper2.call(p1, p2), not(ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, person).call(p2)) )).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftPartialSymbolTransitiveCallTest() { var input = Query.of("Actual", (builder, p1, p2)-> builder.clause( friendMustView.callTransitive(p1, p2), not(person.call(p2)) )).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var endExistsHelper = Query.of("EndExistsHelper", (builder, p1, p2) -> builder.clause( friendMustView.call(p1, p2), ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(p2) )); var transitiveHelper = Query.of("TransitiveHelper", (builder, p1, p2) -> { builder.clause( friendMustView.call(p1, p2) ); builder.clause((v1) -> List.of( endExistsHelper.callTransitive(p1, v1), friendMustView.call(v1, p2) )); }); var expected = Query.of("Expected", (builder, p1, p2) -> builder.clause( transitiveHelper.call(p1, p2), not(ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, person).call(p2)) )).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftPartialRelationTransitiveCallExistsTest() { var input = Query.of("Actual", (builder, p1) -> builder.clause((v1) -> List.of( friend.callTransitive(p1, v1), not(person.call(v1)) ))).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var helper = Query.of("Helper", (builder, p1, p2) -> builder.clause( ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, friend).call(p1, p2), ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(p2) )); var expected = Query.of("Expected", (builder, p1) -> builder.clause((v1) -> List.of( helper.callTransitive(p1, v1), not(ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, person).call(v1)) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftMultipleTransitiveCallExistsTest() { var input = Query.of("Actual", (builder, p1) -> builder.clause((v1) -> List.of( friend.callTransitive(p1, v1), friendMustView.callTransitive(p1, v1), not(person.call(v1)) ))).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var helper = Query.of("Helper", (builder, p1, p2) -> builder.clause( ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, friend).call(p1, p2), ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(p2) )); var helper2 = Query.of("Helper2", (builder, p1, p2) -> builder.clause( friendMustView.call(p1, p2), ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(p2) )); var expected = Query.of("Expected", (builder, p1) -> builder.clause((v1) -> List.of( helper.callTransitive(p1, v1), helper2.callTransitive(p1, v1), not(ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, person).call(v1)) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftEquivalentTest() { var input = Query.of("Actual", (builder, p1, p2) -> builder.clause( p1.isEquivalent(p2), person.call(p1) )).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var expected = Query.of("Expected", (builder, p1, p2) -> builder.clause( ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, person).call(p1), ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, ReasoningAdapter.EQUALS_SYMBOL).call(p2, p1) )).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftNotEquivalentTest() { var input = Query.of("Actual", (builder, p1, p2) -> builder.clause( not(p1.isEquivalent(p2)), friend.call(p1, p2) )).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var expected = Query.of("Expected", (builder, p1, p2) -> builder.clause( ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, friend).call(p1, p2), not(ModalConstraint.of(Modality.MUST, Concreteness.PARTIAL, ReasoningAdapter.EQUALS_SYMBOL).call(p1, p2)) )).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftConstantTest() { var input = Query.of("Actual", (builder, p1) -> builder.clause((v1) -> List.of( v1.isConstant(0), friend.call(v1, p1) ))).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var expected = Query.of("Expected", (builder, p1) -> builder.clause((v1) -> List.of( v1.isConstant(0), ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, friend).call(v1, p1), ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, ReasoningAdapter.EXISTS_SYMBOL).call(v1) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftAssignTest() { var input = Query.of("Actual", Integer.class, (builder, p1, output) -> builder .clause(Integer.class, (d1) -> List.of( person.call(p1), ageView.call(p1, d1), output.assign(mul(constant(2), d1)) ))).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.PARTIAL, input); var expected = Query.of("Expected", Integer.class, (builder, p1, output) -> builder .clause(Integer.class, (d1) -> List.of( ModalConstraint.of(Modality.MAY, Concreteness.PARTIAL, person).call(p1), ageView.call(p1, d1), output.assign(mul(constant(2), d1)) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } @Test void liftCheckTest() { var input = Query.of("Actual", (builder, p1) -> builder.clause(Integer.class, (d1) -> List.of( person.call(p1), ageView.call(p1, d1), check(greaterEq(d1, constant(21))) ))).getDnf(); var actual = sut.lift(Modality.MAY, Concreteness.CANDIDATE, input); var expected = Query.of("Expected", (builder, p1) -> builder.clause(Integer.class, (d1) -> List.of( ModalConstraint.of(Modality.MAY, Concreteness.CANDIDATE, person).call(p1), ageView.call(p1, d1), check(greaterEq(d1, constant(21))) ))).getDnf(); assertThat(actual, structurallyEqualTo(expected)); } }