aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2023-03-07 16:26:26 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2023-03-31 15:38:59 +0200
commit372058e54825ab58a66c25ae528e81a656c22659 (patch)
tree3686057057ebcad2faae7233dc691ecacc3e9fe2
parentrefactor: use Cursor in query result sets (diff)
downloadrefinery-372058e54825ab58a66c25ae528e81a656c22659.tar.gz
refinery-372058e54825ab58a66c25ae528e81a656c22659.tar.zst
refinery-372058e54825ab58a66c25ae528e81a656c22659.zip
feat: terms and improved query evaluation
* Implement data terms for computations in queries. * Function-like queries with computed results. * Improved query evaluation, including positive and negative diagonal cosntraints. * Preliminary local search support. * Changes to the DNF representation for count and aggregation support. feat: terms wip feat: query terms wip feat: query evaluation, diagonal constraints, local search wip fix reasoning compilation wip
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java11
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java51
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java63
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java22
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java19
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java31
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java75
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java116
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java30
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java136
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java55
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java23
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java65
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java48
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java91
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java)2
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java50
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java23
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java15
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ResultSetCursor.java)7
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java89
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java52
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/ViatraTupleLike.java (renamed from subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraTupleLike.java)9
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/AssumptionEvaluator.java16
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java201
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java173
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java1
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java93
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java5
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java60
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java50
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java32
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java14
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java3
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java22
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java6
-rw-r--r--subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java5
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java471
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java607
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java428
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java384
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java52
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java22
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java16
-rw-r--r--subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java19
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/AnyResultSet.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java65
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java22
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryAdapter.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java5
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/RelationLike.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java13
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/Variable.java63
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/AnyQuery.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Dnf.java (renamed from subprojects/store-query/src/main/java/tools/refinery/store/query/Dnf.java)61
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfBuilder.java (renamed from subprojects/store-query/src/main/java/tools/refinery/store/query/DnfBuilder.java)41
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfClause.java (renamed from subprojects/store-query/src/main/java/tools/refinery/store/query/DnfClause.java)5
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfUtils.java (renamed from subprojects/store-query/src/main/java/tools/refinery/store/query/DnfUtils.java)2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalDependency.java (renamed from subprojects/store-query/src/main/java/tools/refinery/store/query/FunctionalDependency.java)2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java103
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQueryBuilder.java46
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/QueryBuilder.java71
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/RelationalQuery.java93
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DeepDnfEqualityChecker.java2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DnfEqualityChecker.java2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/equality/LiteralEqualityHelper.java4
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java80
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java113
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssignLiteral.java44
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssumeLiteral.java53
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/BooleanLiteral.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java105
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CanNegate.java5
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/ConstantLiteral.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CountLiteral.java83
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/DnfCallLiteral.java40
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java17
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literal.java4
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literals.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/PolarLiteral.java5
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/literal/RelationViewLiteral.java35
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/CompositeSubstitution.java10
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/MapBasedSubstitution.java2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/RenewingSubstitution.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/StatelessSubstitution.java2
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitution.java23
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitutions.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/Aggregator.java13
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java33
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyTerm.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryOperator.java26
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryTerm.java56
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryOperator.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryTerm.java51
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/AssignedValue.java8
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/BinaryTerm.java93
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonOperator.java20
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonTerm.java63
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ConstantTerm.java52
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataSort.java29
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java87
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/ExtremeValueAggregator.java103
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeSort.java30
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java48
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/OpaqueTerm.java80
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/Sort.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregate.java17
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregator.java23
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatelessAggregator.java20
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/Term.java21
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/UnaryTerm.java68
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java76
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolConstantTerm.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolLogicBinaryTerm.java78
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolNotTerm.java36
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolTerms.java30
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/LogicBinaryOperator.java17
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticBinaryTerm.java48
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticUnaryTerm.java30
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntComparisonTerm.java34
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntExtremeValueAggregator.java17
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntSumAggregator.java35
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntTerms.java81
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/RealToIntTerm.java36
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/IntToRealTerm.java36
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticBinaryTerm.java36
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticUnaryTerm.java30
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealComparisonTerm.java35
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealExtremeValueAggregator.java17
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealSumAggregator.java85
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealTerms.java81
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/RestrictedValuation.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/SubstitutedValuation.java11
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/Valuation.java23
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/AnyRelationView.java6
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/ForbiddenRelationView.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java38
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/MayRelationView.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/MustRelationView.java16
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/RelationView.java22
-rw-r--r--subprojects/store-query/src/main/java/tools/refinery/store/query/view/TuplePreservingRelationView.java13
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/DnfBuilderTest.java50
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/DnfToDefinitionStringTest.java (renamed from subprojects/store-query/src/test/java/tools/refinery/store/query/DnfToStringTest.java)60
-rw-r--r--subprojects/store-query/src/test/java/tools/refinery/store/query/tests/StructurallyEqualToTest.java22
-rw-r--r--subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/MismatchDescribingDnfEqualityChecker.java8
-rw-r--r--subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/QueryMatchers.java2
-rw-r--r--subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/StructurallyEqualTo.java4
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/PartialInterpretation.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningBuilder.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningStoreAdapter.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningAdapterImpl.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningStoreAdapterImpl.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/DnfLifter.java71
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ModalDnf.java4
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalConstraint.java46
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalDnfCallLiteral.java48
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalLiteral.java63
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalRelationLiteral.java37
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialLiterals.java32
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialRelationLiteral.java32
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/representation/PartialRelation.java36
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RelationRefinementAction.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/Rule.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleAction.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java4
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/UniformSeed.java22
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/Advice.java2
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/TranslatedRelation.java22
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/TranslationUnit.java28
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/BaseDecisionInterpretation.java88
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/BaseDecisionTranslationUnit.java49
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/TranslatedBaseDecision.java49
-rw-r--r--subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslationUnit.java34
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/tuple/TupleLike.java10
-rw-r--r--subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java4
179 files changed, 6782 insertions, 1172 deletions
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
index 24aa52e2..7ae86f9f 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraModelQueryBuilder.java
@@ -4,7 +4,8 @@ import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
4import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 4import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
5import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 5import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
6import tools.refinery.store.model.ModelStore; 6import tools.refinery.store.model.ModelStore;
7import tools.refinery.store.query.Dnf; 7import tools.refinery.store.query.dnf.AnyQuery;
8import tools.refinery.store.query.dnf.Dnf;
8import tools.refinery.store.query.ModelQueryBuilder; 9import tools.refinery.store.query.ModelQueryBuilder;
9 10
10import java.util.Collection; 11import java.util.Collection;
@@ -23,21 +24,21 @@ public interface ViatraModelQueryBuilder extends ModelQueryBuilder {
23 ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory); 24 ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory);
24 25
25 @Override 26 @Override
26 default ViatraModelQueryBuilder queries(Dnf... queries) { 27 default ViatraModelQueryBuilder queries(AnyQuery... queries) {
27 ModelQueryBuilder.super.queries(queries); 28 ModelQueryBuilder.super.queries(queries);
28 return this; 29 return this;
29 } 30 }
30 31
31 @Override 32 @Override
32 default ViatraModelQueryBuilder queries(Collection<Dnf> queries) { 33 default ViatraModelQueryBuilder queries(Collection<? extends AnyQuery> queries) {
33 ModelQueryBuilder.super.queries(queries); 34 ModelQueryBuilder.super.queries(queries);
34 return this; 35 return this;
35 } 36 }
36 37
37 @Override 38 @Override
38 ViatraModelQueryBuilder query(Dnf query); 39 ViatraModelQueryBuilder query(AnyQuery query);
39 40
40 ViatraModelQueryBuilder query(Dnf query, QueryEvaluationHint queryEvaluationHint); 41 ViatraModelQueryBuilder query(AnyQuery query, QueryEvaluationHint queryEvaluationHint);
41 42
42 ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint); 43 ViatraModelQueryBuilder computeHint(Function<Dnf, QueryEvaluationHint> computeHint);
43 44
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
index 37700413..8be30fee 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryAdapterImpl.java
@@ -7,10 +7,18 @@ import org.eclipse.viatra.query.runtime.internal.apiimpl.ViatraQueryEngineImpl;
7import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend; 7import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend;
8import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 8import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
9import tools.refinery.store.model.Model; 9import tools.refinery.store.model.Model;
10import tools.refinery.store.query.Dnf; 10import tools.refinery.store.model.ModelListener;
11import tools.refinery.store.query.AnyResultSet;
11import tools.refinery.store.query.EmptyResultSet; 12import tools.refinery.store.query.EmptyResultSet;
12import tools.refinery.store.query.ResultSet; 13import tools.refinery.store.query.ResultSet;
14import tools.refinery.store.query.dnf.AnyQuery;
15import tools.refinery.store.query.dnf.FunctionalQuery;
16import tools.refinery.store.query.dnf.Query;
17import tools.refinery.store.query.dnf.RelationalQuery;
13import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; 18import tools.refinery.store.query.viatra.ViatraModelQueryAdapter;
19import tools.refinery.store.query.viatra.internal.matcher.FunctionalViatraMatcher;
20import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
21import tools.refinery.store.query.viatra.internal.matcher.RelationalViatraMatcher;
14 22
15import java.lang.invoke.MethodHandle; 23import java.lang.invoke.MethodHandle;
16import java.lang.invoke.MethodHandles; 24import java.lang.invoke.MethodHandles;
@@ -19,7 +27,7 @@ import java.util.Collections;
19import java.util.LinkedHashMap; 27import java.util.LinkedHashMap;
20import java.util.Map; 28import java.util.Map;
21 29
22public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter { 30public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, ModelListener {
23 private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery"; 31 private static final String DELAY_MESSAGE_DELIVERY_FIELD_NAME = "delayMessageDelivery";
24 private static final MethodHandle SET_UPDATE_PROPAGATION_DELAYED_HANDLE; 32 private static final MethodHandle SET_UPDATE_PROPAGATION_DELAYED_HANDLE;
25 private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends"; 33 private static final String QUERY_BACKENDS_FIELD_NAME = "queryBackends";
@@ -28,8 +36,7 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
28 private final Model model; 36 private final Model model;
29 private final ViatraModelQueryStoreAdapterImpl storeAdapter; 37 private final ViatraModelQueryStoreAdapterImpl storeAdapter;
30 private final ViatraQueryEngineImpl queryEngine; 38 private final ViatraQueryEngineImpl queryEngine;
31 39 private final Map<AnyQuery, AnyResultSet> resultSets;
32 private final Map<Dnf, ResultSet> resultSets;
33 private boolean pendingChanges; 40 private boolean pendingChanges;
34 41
35 static { 42 static {
@@ -49,9 +56,8 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
49 this.model = model; 56 this.model = model;
50 this.storeAdapter = storeAdapter; 57 this.storeAdapter = storeAdapter;
51 var scope = new RelationalScope(this); 58 var scope = new RelationalScope(this);
52 queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope); 59 queryEngine = (ViatraQueryEngineImpl) AdvancedViatraQueryEngine.createUnmanagedEngine(scope,
53 60 storeAdapter.getEngineOptions());
54
55 61
56 var querySpecifications = storeAdapter.getQuerySpecifications(); 62 var querySpecifications = storeAdapter.getQuerySpecifications();
57 GenericQueryGroup.of( 63 GenericQueryGroup.of(
@@ -60,14 +66,28 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
60 var vacuousQueries = storeAdapter.getVacuousQueries(); 66 var vacuousQueries = storeAdapter.getVacuousQueries();
61 resultSets = new LinkedHashMap<>(querySpecifications.size() + vacuousQueries.size()); 67 resultSets = new LinkedHashMap<>(querySpecifications.size() + vacuousQueries.size());
62 for (var entry : querySpecifications.entrySet()) { 68 for (var entry : querySpecifications.entrySet()) {
63 var matcher = queryEngine.getMatcher(entry.getValue()); 69 var rawPatternMatcher = queryEngine.getMatcher(entry.getValue());
64 resultSets.put(entry.getKey(), matcher); 70 var query = entry.getKey();
71 resultSets.put(query, createResultSet((Query<?>) query, rawPatternMatcher));
65 } 72 }
66 for (var vacuousQuery : vacuousQueries) { 73 for (var vacuousQuery : vacuousQueries) {
67 resultSets.put(vacuousQuery, new EmptyResultSet()); 74 resultSets.put(vacuousQuery, new EmptyResultSet<>(this, (Query<?>) vacuousQuery));
68 } 75 }
69 76
70 setUpdatePropagationDelayed(true); 77 setUpdatePropagationDelayed(true);
78 model.addListener(this);
79 }
80
81 private <T> ResultSet<T> createResultSet(Query<T> query, RawPatternMatcher matcher) {
82 if (query instanceof RelationalQuery relationalQuery) {
83 @SuppressWarnings("unchecked")
84 var resultSet = (ResultSet<T>) new RelationalViatraMatcher(this, relationalQuery, matcher);
85 return resultSet;
86 } else if (query instanceof FunctionalQuery<T> functionalQuery) {
87 return new FunctionalViatraMatcher<>(this, functionalQuery, matcher);
88 } else {
89 throw new IllegalArgumentException("Unknown query: " + query);
90 }
71 } 91 }
72 92
73 private void setUpdatePropagationDelayed(boolean value) { 93 private void setUpdatePropagationDelayed(boolean value) {
@@ -105,12 +125,14 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
105 } 125 }
106 126
107 @Override 127 @Override
108 public ResultSet getResultSet(Dnf query) { 128 public <T> ResultSet<T> getResultSet(Query<T> query) {
109 var resultSet = resultSets.get(query); 129 var resultSet = resultSets.get(query);
110 if (resultSet == null) { 130 if (resultSet == null) {
111 throw new IllegalArgumentException("No matcher for query %s in model".formatted(query.name())); 131 throw new IllegalArgumentException("No matcher for query %s in model".formatted(query.name()));
112 } 132 }
113 return resultSet; 133 @SuppressWarnings("unchecked")
134 var typedResultSet = (ResultSet<T>) resultSet;
135 return typedResultSet;
114 } 136 }
115 137
116 @Override 138 @Override
@@ -142,4 +164,9 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter {
142 } 164 }
143 pendingChanges = false; 165 pendingChanges = false;
144 } 166 }
167
168 @Override
169 public void afterRestore() {
170 flushChanges();
171 }
145} 172}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
index 13641ace..bfabf26e 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryBuilderImpl.java
@@ -2,33 +2,40 @@ package tools.refinery.store.query.viatra.internal;
2 2
3import org.eclipse.viatra.query.runtime.api.IQuerySpecification; 3import org.eclipse.viatra.query.runtime.api.IQuerySpecification;
4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions; 4import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
5import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory; 5import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHintOptions;
6import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory; 6import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
7import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 7import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
8import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory; 8import org.eclipse.viatra.query.runtime.rete.matcher.ReteBackendFactory;
9import tools.refinery.store.adapter.AbstractModelAdapterBuilder; 9import tools.refinery.store.adapter.AbstractModelAdapterBuilder;
10import tools.refinery.store.model.ModelStore; 10import tools.refinery.store.model.ModelStore;
11import tools.refinery.store.model.ModelStoreBuilder; 11import tools.refinery.store.model.ModelStoreBuilder;
12import tools.refinery.store.query.Dnf; 12import tools.refinery.store.query.dnf.AnyQuery;
13import tools.refinery.store.query.dnf.Dnf;
13import tools.refinery.store.query.viatra.ViatraModelQueryBuilder; 14import tools.refinery.store.query.viatra.ViatraModelQueryBuilder;
15import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction;
16import tools.refinery.store.query.viatra.internal.localsearch.RelationalLocalSearchBackendFactory;
17import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
14import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; 18import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery;
15import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher;
16 19
17import java.util.*; 20import java.util.*;
18import java.util.function.Function; 21import java.util.function.Function;
19 22
20public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder implements ViatraModelQueryBuilder { 23public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder implements ViatraModelQueryBuilder {
21 private ViatraQueryEngineOptions.Builder engineOptionsBuilder; 24 private ViatraQueryEngineOptions.Builder engineOptionsBuilder;
25 private QueryEvaluationHint defaultHint = new QueryEvaluationHint(Map.of(
26 // Use a cost function that ignores the initial (empty) model but allows higher arity input keys.
27 LocalSearchHintOptions.PLANNER_COST_FUNCTION, new FlatCostFunction()
28 ), (IQueryBackendFactory) null);
22 private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery(); 29 private final Dnf2PQuery dnf2PQuery = new Dnf2PQuery();
23 private final Set<Dnf> vacuousQueries = new LinkedHashSet<>(); 30 private final Set<AnyQuery> vacuousQueries = new LinkedHashSet<>();
24 private final Map<Dnf, IQuerySpecification<RawPatternMatcher>> querySpecifications = new LinkedHashMap<>(); 31 private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications = new LinkedHashMap<>();
25 32
26 public ViatraModelQueryBuilderImpl(ModelStoreBuilder storeBuilder) { 33 public ViatraModelQueryBuilderImpl(ModelStoreBuilder storeBuilder) {
27 super(storeBuilder); 34 super(storeBuilder);
28 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder() 35 engineOptionsBuilder = new ViatraQueryEngineOptions.Builder()
29 .withDefaultBackend(ReteBackendFactory.INSTANCE) 36 .withDefaultBackend(ReteBackendFactory.INSTANCE)
30 .withDefaultCachingBackend(ReteBackendFactory.INSTANCE) 37 .withDefaultCachingBackend(ReteBackendFactory.INSTANCE)
31 .withDefaultSearchBackend(LocalSearchGenericBackendFactory.INSTANCE); 38 .withDefaultSearchBackend(RelationalLocalSearchBackendFactory.INSTANCE);
32 } 39 }
33 40
34 @Override 41 @Override
@@ -39,7 +46,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp
39 46
40 @Override 47 @Override
41 public ViatraModelQueryBuilder defaultHint(QueryEvaluationHint queryEvaluationHint) { 48 public ViatraModelQueryBuilder defaultHint(QueryEvaluationHint queryEvaluationHint) {
42 engineOptionsBuilder.withDefaultHint(queryEvaluationHint); 49 defaultHint = defaultHint.overrideBy(queryEvaluationHint);
43 return this; 50 return this;
44 } 51 }
45 52
@@ -62,15 +69,16 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp
62 } 69 }
63 70
64 @Override 71 @Override
65 public ViatraModelQueryBuilder query(Dnf query) { 72 public ViatraModelQueryBuilder query(AnyQuery query) {
66 if (querySpecifications.containsKey(query) || vacuousQueries.contains(query)) { 73 if (querySpecifications.containsKey(query) || vacuousQueries.contains(query)) {
67 // Ignore duplicate queries. 74 // Ignore duplicate queries.
68 return this; 75 return this;
69 } 76 }
70 var reduction = query.getReduction(); 77 var dnf = query.getDnf();
78 var reduction = dnf.getReduction();
71 switch (reduction) { 79 switch (reduction) {
72 case NOT_REDUCIBLE -> { 80 case NOT_REDUCIBLE -> {
73 var pQuery = dnf2PQuery.translate(query); 81 var pQuery = dnf2PQuery.translate(dnf);
74 querySpecifications.put(query, pQuery.build()); 82 querySpecifications.put(query, pQuery.build());
75 } 83 }
76 case ALWAYS_FALSE -> vacuousQueries.add(query); 84 case ALWAYS_FALSE -> vacuousQueries.add(query);
@@ -82,9 +90,9 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp
82 } 90 }
83 91
84 @Override 92 @Override
85 public ViatraModelQueryBuilder query(Dnf query, QueryEvaluationHint queryEvaluationHint) { 93 public ViatraModelQueryBuilder query(AnyQuery query, QueryEvaluationHint queryEvaluationHint) {
94 hint(query.getDnf(), queryEvaluationHint);
86 query(query); 95 query(query);
87 hint(query, queryEvaluationHint);
88 return this; 96 return this;
89 } 97 }
90 98
@@ -96,26 +104,35 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder imp
96 104
97 @Override 105 @Override
98 public ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint) { 106 public ViatraModelQueryBuilder hint(Dnf dnf, QueryEvaluationHint queryEvaluationHint) {
99 var pQuery = dnf2PQuery.getAlreadyTranslated(dnf); 107 dnf2PQuery.hint(dnf, queryEvaluationHint);
100 if (pQuery == null) {
101 if (vacuousQueries.contains(dnf)) {
102 // Ignore hits for queries that will never be executed by the query engine.
103 return this;
104 }
105 throw new IllegalArgumentException(
106 "Cannot specify hint for %s, because it was not added to the query engine".formatted(dnf.name()));
107 }
108 pQuery.setEvaluationHints(pQuery.getEvaluationHints().overrideBy(queryEvaluationHint));
109 return this; 108 return this;
110 } 109 }
111 110
112 @Override 111 @Override
113 public ViatraModelQueryStoreAdapterImpl createStoreAdapter(ModelStore store) { 112 public ViatraModelQueryStoreAdapterImpl createStoreAdapter(ModelStore store) {
114 validateSymbols(store); 113 validateSymbols(store);
115 return new ViatraModelQueryStoreAdapterImpl(store, engineOptionsBuilder.build(), dnf2PQuery.getRelationViews(), 114 dnf2PQuery.assertNoUnusedHints();
115 return new ViatraModelQueryStoreAdapterImpl(store, buildEngineOptions(), dnf2PQuery.getRelationViews(),
116 Collections.unmodifiableMap(querySpecifications), Collections.unmodifiableSet(vacuousQueries)); 116 Collections.unmodifiableMap(querySpecifications), Collections.unmodifiableSet(vacuousQueries));
117 } 117 }
118 118
119 private ViatraQueryEngineOptions buildEngineOptions() {
120 // Workaround: manually override the default backend, because {@link ViatraQueryEngineOptions.Builder}
121 // ignores all backend requirements except {@code SPECIFIC}.
122 switch (defaultHint.getQueryBackendRequirementType()) {
123 case SPECIFIC -> engineOptionsBuilder.withDefaultBackend(defaultHint.getQueryBackendFactory());
124 case DEFAULT_CACHING -> engineOptionsBuilder.withDefaultBackend(
125 engineOptionsBuilder.build().getDefaultCachingBackendFactory());
126 case DEFAULT_SEARCH -> engineOptionsBuilder.withDefaultBackend(
127 engineOptionsBuilder.build().getDefaultSearchBackendFactory());
128 case UNSPECIFIED -> {
129 // Nothing to do, leave the default backend unchanged.
130 }
131 }
132 engineOptionsBuilder.withDefaultHint(defaultHint);
133 return engineOptionsBuilder.build();
134 }
135
119 private void validateSymbols(ModelStore store) { 136 private void validateSymbols(ModelStore store) {
120 var symbols = store.getSymbols(); 137 var symbols = store.getSymbols();
121 for (var relationView : dnf2PQuery.getRelationViews().keySet()) { 138 for (var relationView : dnf2PQuery.getRelationViews().keySet()) {
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
index 00660d0b..04c48c43 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/ViatraModelQueryStoreAdapterImpl.java
@@ -5,9 +5,9 @@ import org.eclipse.viatra.query.runtime.api.ViatraQueryEngineOptions;
5import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 5import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
6import tools.refinery.store.model.Model; 6import tools.refinery.store.model.Model;
7import tools.refinery.store.model.ModelStore; 7import tools.refinery.store.model.ModelStore;
8import tools.refinery.store.query.Dnf; 8import tools.refinery.store.query.dnf.AnyQuery;
9import tools.refinery.store.query.viatra.ViatraModelQueryStoreAdapter; 9import tools.refinery.store.query.viatra.ViatraModelQueryStoreAdapter;
10import tools.refinery.store.query.viatra.internal.pquery.RawPatternMatcher; 10import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
11import tools.refinery.store.query.view.AnyRelationView; 11import tools.refinery.store.query.view.AnyRelationView;
12 12
13import java.util.*; 13import java.util.*;
@@ -16,20 +16,20 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd
16 private final ModelStore store; 16 private final ModelStore store;
17 private final ViatraQueryEngineOptions engineOptions; 17 private final ViatraQueryEngineOptions engineOptions;
18 private final Map<AnyRelationView, IInputKey> inputKeys; 18 private final Map<AnyRelationView, IInputKey> inputKeys;
19 private final Map<Dnf, IQuerySpecification<RawPatternMatcher>> querySpecifications; 19 private final Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications;
20 private final Set<Dnf> vacuousQueries; 20 private final Set<AnyQuery> vacuousQueries;
21 private final Set<Dnf> allQueries; 21 private final Set<AnyQuery> allQueries;
22 22
23 ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, 23 ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions,
24 Map<AnyRelationView, IInputKey> inputKeys, 24 Map<AnyRelationView, IInputKey> inputKeys,
25 Map<Dnf, IQuerySpecification<RawPatternMatcher>> querySpecifications, 25 Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> querySpecifications,
26 Set<Dnf> vacuousQueries) { 26 Set<AnyQuery> vacuousQueries) {
27 this.store = store; 27 this.store = store;
28 this.engineOptions = engineOptions; 28 this.engineOptions = engineOptions;
29 this.inputKeys = inputKeys; 29 this.inputKeys = inputKeys;
30 this.querySpecifications = querySpecifications; 30 this.querySpecifications = querySpecifications;
31 this.vacuousQueries = vacuousQueries; 31 this.vacuousQueries = vacuousQueries;
32 var mutableAllQueries = new LinkedHashSet<Dnf>(querySpecifications.size() + vacuousQueries.size()); 32 var mutableAllQueries = new LinkedHashSet<AnyQuery>(querySpecifications.size() + vacuousQueries.size());
33 mutableAllQueries.addAll(querySpecifications.keySet()); 33 mutableAllQueries.addAll(querySpecifications.keySet());
34 mutableAllQueries.addAll(vacuousQueries); 34 mutableAllQueries.addAll(vacuousQueries);
35 this.allQueries = Collections.unmodifiableSet(mutableAllQueries); 35 this.allQueries = Collections.unmodifiableSet(mutableAllQueries);
@@ -49,15 +49,15 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd
49 } 49 }
50 50
51 @Override 51 @Override
52 public Collection<Dnf> getQueries() { 52 public Collection<AnyQuery> getQueries() {
53 return allQueries; 53 return allQueries;
54 } 54 }
55 55
56 Map<Dnf, IQuerySpecification<RawPatternMatcher>> getQuerySpecifications() { 56 Map<AnyQuery, IQuerySpecification<RawPatternMatcher>> getQuerySpecifications() {
57 return querySpecifications; 57 return querySpecifications;
58 } 58 }
59 59
60 Set<Dnf> getVacuousQueries() { 60 Set<AnyQuery> getVacuousQueries() {
61 return vacuousQueries; 61 return vacuousQueries;
62 } 62 }
63 63
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
index cba3fa08..d2c6beb4 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalQueryMetaContext.java
@@ -3,6 +3,8 @@ package tools.refinery.store.query.viatra.internal.context;
3import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext; 3import org.eclipse.viatra.query.runtime.matchers.context.AbstractQueryMetaContext;
4import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 4import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
5import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication; 5import org.eclipse.viatra.query.runtime.matchers.context.InputKeyImplication;
6import org.eclipse.viatra.query.runtime.matchers.context.common.JavaTransitiveInstancesKey;
7import tools.refinery.store.query.term.DataSort;
6import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper; 8import tools.refinery.store.query.viatra.internal.pquery.RelationViewWrapper;
7import tools.refinery.store.query.view.AnyRelationView; 9import tools.refinery.store.query.view.AnyRelationView;
8 10
@@ -37,6 +39,9 @@ public class RelationalQueryMetaContext extends AbstractQueryMetaContext {
37 39
38 @Override 40 @Override
39 public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) { 41 public Collection<InputKeyImplication> getImplications(IInputKey implyingKey) {
42 if (implyingKey instanceof JavaTransitiveInstancesKey) {
43 return List.of();
44 }
40 var relationView = checkKey(implyingKey); 45 var relationView = checkKey(implyingKey);
41 var relationViewImplications = relationView.getImpliedRelationViews(); 46 var relationViewImplications = relationView.getImpliedRelationViews();
42 var inputKeyImplications = new HashSet<InputKeyImplication>(relationViewImplications.size()); 47 var inputKeyImplications = new HashSet<InputKeyImplication>(relationViewImplications.size());
@@ -52,11 +57,25 @@ public class RelationalQueryMetaContext extends AbstractQueryMetaContext {
52 relationViewImplication.impliedIndices())); 57 relationViewImplication.impliedIndices()));
53 } 58 }
54 } 59 }
60 var sorts = relationView.getSorts();
61 int arity = relationView.arity();
62 for (int i = 0; i < arity; i++) {
63 var sort = sorts.get(i);
64 if (sort instanceof DataSort<?> dataSort) {
65 var javaTransitiveInstancesKey = new JavaTransitiveInstancesKey(dataSort.type());
66 var javaImplication = new InputKeyImplication(implyingKey, javaTransitiveInstancesKey,
67 List.of(i));
68 inputKeyImplications.add(javaImplication);
69 }
70 }
55 return inputKeyImplications; 71 return inputKeyImplications;
56 } 72 }
57 73
58 @Override 74 @Override
59 public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) { 75 public Map<Set<Integer>, Set<Integer>> getFunctionalDependencies(IInputKey key) {
76 if (key instanceof JavaTransitiveInstancesKey) {
77 return Map.of();
78 }
60 var relationView = checkKey(key); 79 var relationView = checkKey(key);
61 var functionalDependencies = relationView.getFunctionalDependencies(); 80 var functionalDependencies = relationView.getFunctionalDependencies();
62 var flattened = new HashMap<Set<Integer>, Set<Integer>>(functionalDependencies.size()); 81 var flattened = new HashMap<Set<Integer>, Set<Integer>>(functionalDependencies.size());
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
index 01d20d3e..854817b1 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/context/RelationalRuntimeContext.java
@@ -54,7 +54,8 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
54 54
55 @Override 55 @Override
56 public boolean isIndexed(IInputKey key, IndexingService service) { 56 public boolean isIndexed(IInputKey key, IndexingService service) {
57 if (key instanceof AnyRelationView relationalKey) { 57 if (key instanceof RelationViewWrapper wrapper) {
58 var relationalKey = wrapper.getWrappedKey();
58 return this.modelUpdateListener.containsRelationView(relationalKey); 59 return this.modelUpdateListener.containsRelationView(relationalKey);
59 } else { 60 } else {
60 return false; 61 return false;
@@ -83,10 +84,7 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
83 84
84 @Override 85 @Override
85 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) { 86 public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
86 var relationViewKey = checkKey(key); 87 Iterator<Object[]> iterator = enumerate(key, seedMask, seed).iterator();
87 Iterable<Object[]> allObjects = relationViewKey.getAll(model);
88 Iterable<Object[]> filteredBySeed = filter(allObjects, objectArray -> isMatching(objectArray, seedMask, seed));
89 Iterator<Object[]> iterator = filteredBySeed.iterator();
90 int result = 0; 88 int result = 0;
91 while (iterator.hasNext()) { 89 while (iterator.hasNext()) {
92 iterator.next(); 90 iterator.next();
@@ -102,13 +100,25 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
102 100
103 @Override 101 @Override
104 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) { 102 public Iterable<Tuple> enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed) {
103 var filteredBySeed = enumerate(key, seedMask, seed);
104 return map(filteredBySeed, Tuples::flatTupleOf);
105 }
106
107 @Override
108 public Iterable<?> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) {
109 var index = seedMask.getFirstOmittedIndex().orElseThrow(
110 () -> new IllegalArgumentException("Seed mask does not omit a value"));
111 var filteredBySeed = enumerate(key, seedMask, seed);
112 return map(filteredBySeed, array -> array[index]);
113 }
114
115 private Iterable<Object[]> enumerate(IInputKey key, TupleMask seedMask, ITuple seed) {
105 var relationViewKey = checkKey(key); 116 var relationViewKey = checkKey(key);
106 Iterable<Object[]> allObjects = relationViewKey.getAll(model); 117 Iterable<Object[]> allObjects = relationViewKey.getAll(model);
107 Iterable<Object[]> filteredBySeed = filter(allObjects, objectArray -> isMatching(objectArray, seedMask, seed)); 118 return filter(allObjects, objectArray -> isMatching(objectArray, seedMask, seed));
108 return map(filteredBySeed, Tuples::flatTupleOf);
109 } 119 }
110 120
111 private boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) { 121 private static boolean isMatching(Object[] tuple, TupleMask seedMask, ITuple seed) {
112 for (int i = 0; i < seedMask.indices.length; i++) { 122 for (int i = 0; i < seedMask.indices.length; i++) {
113 final Object seedElement = seed.get(i); 123 final Object seedElement = seed.get(i);
114 final Object tupleElement = tuple[seedMask.indices[i]]; 124 final Object tupleElement = tuple[seedMask.indices[i]];
@@ -120,11 +130,6 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext {
120 } 130 }
121 131
122 @Override 132 @Override
123 public Iterable<?> enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed) {
124 return enumerateTuples(key, seedMask, seed);
125 }
126
127 @Override
128 public boolean containsTuple(IInputKey key, ITuple seed) { 133 public boolean containsTuple(IInputKey key, ITuple seed) {
129 var relationViewKey = checkKey(key); 134 var relationViewKey = checkKey(key);
130 return relationViewKey.get(model, seed.getElements()); 135 return relationViewKey.get(model, seed.getElements());
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java
new file mode 100644
index 00000000..2d3dd5a7
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendOperationExecutor.java
@@ -0,0 +1,75 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2013, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 * SPDX-License-Identifier: EPL-2.0
7 *******************************************************************************/
8package tools.refinery.store.query.viatra.internal.localsearch;
9
10import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame;
11import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext;
12import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation.ISearchOperationExecutor;
13
14import java.util.Iterator;
15
16/**
17 * An operation that can be used to enumerate all possible values for a single position based on a constraint
18 * @author Zoltan Ujhelyi, Akos Horvath
19 * @since 2.0
20 */
21abstract class ExtendOperationExecutor<T> implements ISearchOperationExecutor {
22
23 private Iterator<? extends T> it;
24
25 /**
26 * Returns an iterator with the possible options from the current state
27 * @since 2.0
28 */
29 @SuppressWarnings("squid:S1452")
30 protected abstract Iterator<? extends T> getIterator(MatchingFrame frame, ISearchContext context);
31 /**
32 * Updates the frame with the next element of the iterator. Called during {@link #execute(MatchingFrame, ISearchContext)}.
33 *
34 * @return true if the update is successful or false otherwise; in case of false is returned, the next element should be taken from the iterator.
35 * @since 2.0
36 */
37 protected abstract boolean fillInValue(T newValue, MatchingFrame frame, ISearchContext context);
38
39 /**
40 * Restores the frame to the state before {@link #fillInValue(Object, MatchingFrame, ISearchContext)}. Called during
41 * {@link #onBacktrack(MatchingFrame, ISearchContext)}.
42 *
43 * @since 2.0
44 */
45 protected abstract void cleanup(MatchingFrame frame, ISearchContext context);
46
47 @Override
48 public void onInitialize(MatchingFrame frame, ISearchContext context) {
49 it = getIterator(frame, context);
50 }
51
52 @Override
53 public void onBacktrack(MatchingFrame frame, ISearchContext context) {
54 it = null;
55
56 }
57
58 /**
59 * Fixed version of {@link org.eclipse.viatra.query.runtime.localsearch.operations.ExtendOperationExecutor#execute}
60 * that handles failed unification of variables correctly.
61 * @param frame The matching frame to extend.
62 * @param context The search context.
63 * @return {@code true} if an extension was found, {@code false} otherwise.
64 */
65 @Override
66 public boolean execute(MatchingFrame frame, ISearchContext context) {
67 while (it.hasNext()) {
68 var newValue = it.next();
69 if (fillInValue(newValue, frame, context)) {
70 return true;
71 }
72 }
73 return false;
74 }
75}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java
new file mode 100644
index 00000000..aaaece80
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/ExtendPositivePatternCall.java
@@ -0,0 +1,116 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 * SPDX-License-Identifier: EPL-2.0
7 *******************************************************************************/
8package tools.refinery.store.query.viatra.internal.localsearch;
9
10import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame;
11import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext;
12import org.eclipse.viatra.query.runtime.localsearch.operations.IPatternMatcherOperation;
13import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation;
14import org.eclipse.viatra.query.runtime.localsearch.operations.util.CallInformation;
15import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
16import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
17import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
18import org.eclipse.viatra.query.runtime.matchers.tuple.VolatileModifiableMaskedTuple;
19
20import java.util.Iterator;
21import java.util.List;
22import java.util.function.Function;
23
24/**
25 * @author Grill Balázs
26 * @since 1.4
27 *
28 */
29public class ExtendPositivePatternCall implements ISearchOperation, IPatternMatcherOperation {
30
31 private class Executor extends ExtendOperationExecutor<Tuple> {
32 private final VolatileModifiableMaskedTuple maskedTuple;
33
34 public Executor() {
35 maskedTuple = new VolatileModifiableMaskedTuple(information.getThinFrameMask());
36 }
37
38 @Override
39 protected Iterator<? extends Tuple> getIterator(MatchingFrame frame, ISearchContext context) {
40 maskedTuple.updateTuple(frame);
41 IQueryResultProvider matcher = context.getMatcher(information.getCallWithAdornment());
42 return matcher.getAllMatches(information.getParameterMask(), maskedTuple).iterator();
43 }
44
45 /**
46 * @since 2.0
47 */
48 @Override
49 protected boolean fillInValue(Tuple result, MatchingFrame frame, ISearchContext context) {
50 TupleMask mask = information.getFullFrameMask();
51 // The first loop clears out the elements from a possible previous iteration
52 for(int i : information.getFreeParameterIndices()) {
53 mask.set(frame, i, null);
54 }
55 for(int i : information.getFreeParameterIndices()) {
56 Object oldValue = mask.getValue(frame, i);
57 Object valueToFill = result.get(i);
58 if (oldValue != null && !oldValue.equals(valueToFill)){
59 // If the inverse map contains more than one values for the same key, it means that these arguments are unified by the caller.
60 // In this case if the callee assigns different values the frame shall be dropped
61 return false;
62 }
63 mask.set(frame, i, valueToFill);
64 }
65 return true;
66 }
67
68 @Override
69 protected void cleanup(MatchingFrame frame, ISearchContext context) {
70 TupleMask mask = information.getFullFrameMask();
71 for(int i : information.getFreeParameterIndices()){
72 mask.set(frame, i, null);
73 }
74
75 }
76
77 @Override
78 public ISearchOperation getOperation() {
79 return ExtendPositivePatternCall.this;
80 }
81 }
82
83 private final CallInformation information;
84
85 /**
86 * @since 1.7
87 */
88 public ExtendPositivePatternCall(CallInformation information) {
89 this.information = information;
90 }
91
92 @Override
93 public ISearchOperationExecutor createExecutor() {
94 return new Executor();
95 }
96
97 @Override
98 public List<Integer> getVariablePositions() {
99 return information.getVariablePositions();
100 }
101
102 @Override
103 public String toString() {
104 return toString(Object::toString);
105 }
106
107 @Override
108 public String toString(@SuppressWarnings("squid:S4276") Function<Integer, String> variableMapping) {
109 return "extend find " + information.toString(variableMapping);
110 }
111
112 @Override
113 public CallInformation getCallInformation() {
114 return information;
115 }
116}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java
new file mode 100644
index 00000000..84cab142
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/FlatCostFunction.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.viatra.internal.localsearch;
2
3import org.eclipse.viatra.query.runtime.localsearch.planner.cost.IConstraintEvaluationContext;
4import org.eclipse.viatra.query.runtime.localsearch.planner.cost.impl.StatisticsBasedConstraintCostFunction;
5import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
6import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
7import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
8import org.eclipse.viatra.query.runtime.matchers.util.Accuracy;
9
10import java.util.Optional;
11
12public class FlatCostFunction extends StatisticsBasedConstraintCostFunction {
13 public FlatCostFunction() {
14 // No inverse navigation penalty thanks to relational storage.
15 super(0);
16 }
17
18 @Override
19 public Optional<Long> projectionSize(IConstraintEvaluationContext input, IInputKey supplierKey, TupleMask groupMask, Accuracy requiredAccuracy) {
20 // We always start from an empty model, where every projection is of size 0.
21 // Therefore, projection size estimation is meaningless.
22 return Optional.empty();
23 }
24
25 @Override
26 protected double _calculateCost(TypeConstraint constraint, IConstraintEvaluationContext input) {
27 // Assume a flat cost for each relation. Maybe adjust in the future if we perform indexing?
28 return DEFAULT_COST;
29 }
30}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java
new file mode 100644
index 00000000..64653658
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/GenericTypeExtend.java
@@ -0,0 +1,136 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd.
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 * SPDX-License-Identifier: EPL-2.0
7 *******************************************************************************/
8package tools.refinery.store.query.viatra.internal.localsearch;
9
10import org.eclipse.viatra.query.runtime.localsearch.MatchingFrame;
11import org.eclipse.viatra.query.runtime.localsearch.matcher.ISearchContext;
12import org.eclipse.viatra.query.runtime.localsearch.operations.IIteratingSearchOperation;
13import org.eclipse.viatra.query.runtime.localsearch.operations.ISearchOperation;
14import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
15import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
16import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
17import org.eclipse.viatra.query.runtime.matchers.tuple.VolatileMaskedTuple;
18import org.eclipse.viatra.query.runtime.matchers.util.Preconditions;
19
20import java.util.*;
21import java.util.function.Function;
22import java.util.stream.Collectors;
23
24/**
25 * @author Zoltan Ujhelyi
26 * @since 1.7
27 */
28public class GenericTypeExtend implements IIteratingSearchOperation {
29 private class Executor extends ExtendOperationExecutor<Tuple> {
30 private final VolatileMaskedTuple maskedTuple;
31
32 public Executor() {
33 this.maskedTuple = new VolatileMaskedTuple(callMask);
34 }
35
36 @Override
37 protected Iterator<? extends Tuple> getIterator(MatchingFrame frame, ISearchContext context) {
38 maskedTuple.updateTuple(frame);
39 return context.getRuntimeContext().enumerateTuples(type, indexerMask, maskedTuple).iterator();
40 }
41
42 @Override
43 protected boolean fillInValue(Tuple newTuple, MatchingFrame frame, ISearchContext context) {
44 for (Integer position : unboundVariableIndices) {
45 frame.setValue(position, null);
46 }
47 for (int i = 0; i < positions.length; i++) {
48 Object newValue = newTuple.get(i);
49 Object oldValue = frame.getValue(positions[i]);
50 if (oldValue != null && !Objects.equals(oldValue, newValue)) {
51 // If positions tuple maps more than one values for the same element (e.g. loop), it means that
52 // these arguments are to unified by the caller. In this case if the callee assigns different values
53 // the frame shall be considered a failed match
54 return false;
55 }
56 frame.setValue(positions[i], newValue);
57 }
58 return true;
59 }
60
61 @Override
62 protected void cleanup(MatchingFrame frame, ISearchContext context) {
63 for (Integer position : unboundVariableIndices) {
64 frame.setValue(position, null);
65 }
66 }
67
68 @Override
69 public ISearchOperation getOperation() {
70 return GenericTypeExtend.this;
71 }
72 }
73
74 private final IInputKey type;
75 private final int[] positions;
76 private final List<Integer> positionList;
77 private final Set<Integer> unboundVariableIndices;
78 private final TupleMask indexerMask;
79 private final TupleMask callMask;
80
81 /**
82 *
83 * @param type
84 * the type to execute the extend operation on
85 * @param positions
86 * the parameter positions that represent the variables of the input key
87 * @param unboundVariableIndices
88 * the set of positions that are bound at the start of the operation
89 */
90 public GenericTypeExtend(IInputKey type, int[] positions, TupleMask callMask, TupleMask indexerMask, Set<Integer> unboundVariableIndices) {
91 Preconditions.checkArgument(positions.length == type.getArity(),
92 "The type %s requires %d parameters, but %d positions are provided", type.getPrettyPrintableName(),
93 type.getArity(), positions.length);
94 List<Integer> modifiablePositionList = new ArrayList<>();
95 for (int position : positions) {
96 modifiablePositionList.add(position);
97 }
98 this.positionList = Collections.unmodifiableList(modifiablePositionList);
99 this.positions = positions;
100 this.type = type;
101
102 this.unboundVariableIndices = unboundVariableIndices;
103 this.indexerMask = indexerMask;
104 this.callMask = callMask;
105 }
106
107 @Override
108 public IInputKey getIteratedInputKey() {
109 return type;
110 }
111
112 @Override
113 public ISearchOperationExecutor createExecutor() {
114 return new Executor();
115 }
116
117 @Override
118 public List<Integer> getVariablePositions() {
119 return positionList;
120 }
121
122 @Override
123 public String toString() {
124 return toString(Object::toString);
125 }
126
127 @Override
128 public String toString(@SuppressWarnings("squid:S4276") Function<Integer, String> variableMapping) {
129 return "extend " + type.getPrettyPrintableName() + "("
130 + positionList.stream()
131 .map(input -> String.format("%s%s", unboundVariableIndices.contains(input) ? "-" : "+", variableMapping.apply(input)))
132 .collect(Collectors.joining(", "))
133 + ")";
134 }
135
136}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java
new file mode 100644
index 00000000..156eb313
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchBackendFactory.java
@@ -0,0 +1,55 @@
1package tools.refinery.store.query.viatra.internal.localsearch;
2
3import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider;
4import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchBackend;
5import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHints;
6import org.eclipse.viatra.query.runtime.localsearch.plan.IPlanProvider;
7import org.eclipse.viatra.query.runtime.localsearch.plan.SimplePlanProvider;
8import org.eclipse.viatra.query.runtime.matchers.backend.IMatcherCapability;
9import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackend;
10import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
11import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
12import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext;
13import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
14
15public class RelationalLocalSearchBackendFactory implements IQueryBackendFactory {
16 public static final RelationalLocalSearchBackendFactory INSTANCE = new RelationalLocalSearchBackendFactory();
17
18 private RelationalLocalSearchBackendFactory() {
19 }
20
21 @Override
22 public IQueryBackend create(IQueryBackendContext context) {
23 return new LocalSearchBackend(context) {
24 // Create a new {@link IPlanProvider}, because the original {@link LocalSearchBackend#planProvider} is not
25 // accessible.
26 private final IPlanProvider planProvider = new SimplePlanProvider(context.getLogger());
27
28 @Override
29 protected AbstractLocalSearchResultProvider initializeResultProvider(PQuery query,
30 QueryEvaluationHint hints) {
31 return new RelationalLocalSearchResultProvider(this, context, query, planProvider, hints);
32 }
33
34 @Override
35 public IQueryBackendFactory getFactory() {
36 return RelationalLocalSearchBackendFactory.this;
37 }
38 };
39 }
40
41 @Override
42 public Class<? extends IQueryBackend> getBackendClass() {
43 return LocalSearchBackend.class;
44 }
45
46 @Override
47 public IMatcherCapability calculateRequiredCapability(PQuery pQuery, QueryEvaluationHint queryEvaluationHint) {
48 return LocalSearchHints.parse(queryEvaluationHint);
49 }
50
51 @Override
52 public boolean isCaching() {
53 return false;
54 }
55}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java
new file mode 100644
index 00000000..be2a38ca
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalLocalSearchResultProvider.java
@@ -0,0 +1,23 @@
1package tools.refinery.store.query.viatra.internal.localsearch;
2
3import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.AbstractLocalSearchResultProvider;
4import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchBackend;
5import org.eclipse.viatra.query.runtime.localsearch.matcher.integration.LocalSearchHints;
6import org.eclipse.viatra.query.runtime.localsearch.plan.IPlanProvider;
7import org.eclipse.viatra.query.runtime.localsearch.planner.compiler.IOperationCompiler;
8import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
9import org.eclipse.viatra.query.runtime.matchers.context.IQueryBackendContext;
10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
11
12class RelationalLocalSearchResultProvider extends AbstractLocalSearchResultProvider {
13 public RelationalLocalSearchResultProvider(LocalSearchBackend backend, IQueryBackendContext context, PQuery query,
14 IPlanProvider planProvider, QueryEvaluationHint userHints) {
15 super(backend, context, query, planProvider, userHints);
16 }
17
18 @Override
19 protected IOperationCompiler getOperationCompiler(IQueryBackendContext backendContext,
20 LocalSearchHints configuration) {
21 return new RelationalOperationCompiler(runtimeContext);
22 }
23}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java
new file mode 100644
index 00000000..b6832b39
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/localsearch/RelationalOperationCompiler.java
@@ -0,0 +1,65 @@
1package tools.refinery.store.query.viatra.internal.localsearch;
2
3import org.eclipse.viatra.query.runtime.localsearch.operations.generic.GenericTypeExtendSingleValue;
4import org.eclipse.viatra.query.runtime.localsearch.operations.util.CallInformation;
5import org.eclipse.viatra.query.runtime.localsearch.planner.compiler.GenericOperationCompiler;
6import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
7import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext;
8import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
9import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
10import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
11import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
12import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
13
14import java.util.*;
15
16public class RelationalOperationCompiler extends GenericOperationCompiler {
17 public RelationalOperationCompiler(IQueryRuntimeContext runtimeContext) {
18 super(runtimeContext);
19 }
20
21 @Override
22 protected void createExtend(TypeConstraint typeConstraint, Map<PVariable, Integer> variableMapping) {
23 IInputKey inputKey = typeConstraint.getSupplierKey();
24 Tuple tuple = typeConstraint.getVariablesTuple();
25
26 int[] positions = new int[tuple.getSize()];
27 List<Integer> boundVariableIndices = new ArrayList<>();
28 List<Integer> boundVariables = new ArrayList<>();
29 Set<Integer> unboundVariables = new HashSet<>();
30 for (int i = 0; i < tuple.getSize(); i++) {
31 PVariable variable = (PVariable) tuple.get(i);
32 Integer position = variableMapping.get(variable);
33 positions[i] = position;
34 if (variableBindings.get(typeConstraint).contains(position)) {
35 boundVariableIndices.add(i);
36 boundVariables.add(position);
37 } else {
38 unboundVariables.add(position);
39 }
40 }
41 TupleMask indexerMask = TupleMask.fromSelectedIndices(inputKey.getArity(), boundVariableIndices);
42 TupleMask callMask = TupleMask.fromSelectedIndices(variableMapping.size(), boundVariables);
43 // If multiple tuple elements from the indexer should be bound to the same variable, we must use a
44 // {@link GenericTypeExtend} check whether the tuple elements have the same value.
45 if (unboundVariables.size() == 1 && indexerMask.getSize() + 1 == indexerMask.getSourceWidth()) {
46 operations.add(new GenericTypeExtendSingleValue(inputKey, positions, callMask, indexerMask,
47 unboundVariables.iterator().next()));
48 } else {
49 // Use a fixed version of
50 // {@code org.eclipse.viatra.query.runtime.localsearch.operations.generic.GenericTypeExtend} that handles
51 // failed unification of variables correctly.
52 operations.add(new GenericTypeExtend(inputKey, positions, callMask, indexerMask, unboundVariables));
53 }
54 }
55
56 @Override
57 protected void createExtend(PositivePatternCall pCall, Map<PVariable, Integer> variableMapping) {
58 CallInformation information = CallInformation.create(pCall, variableMapping, variableBindings.get(pCall));
59 // Use a fixed version of
60 // {@code org.eclipse.viatra.query.runtime.localsearch.operations.extend.ExtendPositivePatternCall} that handles
61 // failed unification of variables correctly.
62 operations.add(new ExtendPositivePatternCall(information));
63 dependencies.add(information.getCallWithAdornment());
64 }
65}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java
new file mode 100644
index 00000000..4daa14a1
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalCursor.java
@@ -0,0 +1,48 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
4import org.eclipse.viatra.query.runtime.rete.index.IterableIndexer;
5import tools.refinery.store.map.Cursor;
6import tools.refinery.store.tuple.TupleLike;
7
8import java.util.Iterator;
9
10class FunctionalCursor<T> implements Cursor<TupleLike, T> {
11 private final IterableIndexer indexer;
12 private final Iterator<Tuple> iterator;
13 private boolean terminated;
14 private TupleLike key;
15 private T value;
16
17 public FunctionalCursor(IterableIndexer indexer) {
18 this.indexer = indexer;
19 iterator = indexer.getSignatures().iterator();
20 }
21
22 @Override
23 public TupleLike getKey() {
24 return key;
25 }
26
27 @Override
28 public T getValue() {
29 return value;
30 }
31
32 @Override
33 public boolean isTerminated() {
34 return terminated;
35 }
36
37 @Override
38 public boolean move() {
39 if (!terminated && iterator.hasNext()) {
40 var match = iterator.next();
41 key = new ViatraTupleLike(match);
42 value = MatcherUtils.getSingleValue(indexer.get(match));
43 return true;
44 }
45 terminated = true;
46 return false;
47 }
48}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java
new file mode 100644
index 00000000..6aa45af2
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/FunctionalViatraMatcher.java
@@ -0,0 +1,91 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
4import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
5import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
6import org.eclipse.viatra.query.runtime.rete.index.IterableIndexer;
7import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.query.ModelQueryAdapter;
10import tools.refinery.store.query.ResultSet;
11import tools.refinery.store.query.dnf.FunctionalQuery;
12import tools.refinery.store.query.dnf.Query;
13import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
14import tools.refinery.store.tuple.TupleLike;
15
16/**
17 * Directly access the tuples inside a VIATRA pattern matcher.<p>
18 * This class neglects calling
19 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}
20 * and
21 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)},
22 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial
23 * implementation for these methods.
24 * Using this class with any other runtime context may lead to undefined behavior.
25 */
26public class FunctionalViatraMatcher<T> implements ResultSet<T> {
27 private final ViatraModelQueryAdapterImpl adapter;
28 private final FunctionalQuery<T> query;
29 private final TupleMask emptyMask;
30 private final TupleMask omitOutputMask;
31 private final IQueryResultProvider backend;
32 private final IterableIndexer omitOutputIndexer;
33
34 public FunctionalViatraMatcher(ViatraModelQueryAdapterImpl adapter, FunctionalQuery<T> query,
35 RawPatternMatcher rawPatternMatcher) {
36 this.adapter = adapter;
37 this.query = query;
38 int arity = query.arity();
39 int arityWithOutput = arity + 1;
40 emptyMask = TupleMask.empty(arityWithOutput);
41 omitOutputMask = TupleMask.omit(arity, arityWithOutput);
42 backend = rawPatternMatcher.getBackend();
43 if (backend instanceof RetePatternMatcher reteBackend) {
44 var maybeIterableOmitOutputIndexer = IndexerUtils.getIndexer(reteBackend, omitOutputMask);
45 if (maybeIterableOmitOutputIndexer instanceof IterableIndexer iterableOmitOutputIndexer) {
46 omitOutputIndexer = iterableOmitOutputIndexer;
47 } else {
48 omitOutputIndexer = null;
49 }
50 } else {
51 omitOutputIndexer = null;
52 }
53 }
54
55 @Override
56 public ModelQueryAdapter getAdapter() {
57 return adapter;
58 }
59
60 @Override
61 public Query<T> getQuery() {
62 return query;
63 }
64
65 @Override
66 public T get(TupleLike parameters) {
67 var tuple = MatcherUtils.toViatraTuple(parameters);
68 if (omitOutputIndexer == null) {
69 return MatcherUtils.getSingleValue(backend.getAllMatches(omitOutputMask, tuple).iterator());
70 } else {
71 return MatcherUtils.getSingleValue(omitOutputIndexer.get(tuple));
72 }
73 }
74
75 @Override
76 public Cursor<TupleLike, T> getAll() {
77 if (omitOutputIndexer == null) {
78 var allMatches = backend.getAllMatches(emptyMask, Tuples.staticArityFlatTupleOf());
79 return new UnsafeFunctionalCursor<>(allMatches.iterator());
80 }
81 return new FunctionalCursor<>(omitOutputIndexer);
82 }
83
84 @Override
85 public int size() {
86 if (omitOutputIndexer == null) {
87 return backend.countMatches(emptyMask, Tuples.staticArityFlatTupleOf());
88 }
89 return omitOutputIndexer.getBucketCount();
90 }
91}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java
index 75588b81..55eb8c44 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/IndexerUtils.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/IndexerUtils.java
@@ -1,4 +1,4 @@
1package tools.refinery.store.query.viatra.internal.pquery; 1package tools.refinery.store.query.viatra.internal.matcher;
2 2
3import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask; 3import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
4import org.eclipse.viatra.query.runtime.rete.index.Indexer; 4import org.eclipse.viatra.query.runtime.rete.index.Indexer;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java
new file mode 100644
index 00000000..5d4be95d
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/MatcherUtils.java
@@ -0,0 +1,50 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
5import org.jetbrains.annotations.Nullable;
6import tools.refinery.store.tuple.Tuple;
7import tools.refinery.store.tuple.TupleLike;
8
9import java.util.Iterator;
10
11final class MatcherUtils {
12 private MatcherUtils() {
13 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
14 }
15
16 public static org.eclipse.viatra.query.runtime.matchers.tuple.Tuple toViatraTuple(TupleLike tuple) {
17 if (tuple instanceof ViatraTupleLike viatraTupleLike) {
18 return viatraTupleLike.wrappedTuple().toImmutable();
19 }
20 int size = tuple.getSize();
21 var array = new Object[size];
22 for (int i = 0; i < size; i++) {
23 var value = tuple.get(i);
24 array[i] = Tuple.of(value);
25 }
26 return Tuples.flatTupleOf(array);
27 }
28
29
30 public static <T> T getSingleValue(@Nullable Iterable<? extends ITuple> tuples) {
31 if (tuples == null) {
32 return null;
33 }
34 return getSingleValue(tuples.iterator());
35 }
36
37 public static <T> T getSingleValue(Iterator<? extends ITuple> iterator) {
38 if (!iterator.hasNext()) {
39 return null;
40 }
41 var match = iterator.next();
42 @SuppressWarnings("unchecked")
43 var result = (T) match.get(match.getSize() - 1);
44 if (iterator.hasNext()) {
45 var input = new OmitOutputViatraTupleLike(match);
46 throw new IllegalStateException("Query is not functional for input tuple: " + input);
47 }
48 return result;
49 }
50}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java
new file mode 100644
index 00000000..bd9301ba
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/OmitOutputViatraTupleLike.java
@@ -0,0 +1,23 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import tools.refinery.store.tuple.Tuple1;
5import tools.refinery.store.tuple.TupleLike;
6
7record OmitOutputViatraTupleLike(ITuple wrappedTuple) implements TupleLike {
8 @Override
9 public int getSize() {
10 return wrappedTuple.getSize() - 1;
11 }
12
13 @Override
14 public int get(int element) {
15 var wrappedValue = (Tuple1) wrappedTuple.get(element);
16 return wrappedValue.value0();
17 }
18
19 @Override
20 public String toString() {
21 return TupleLike.toString(this);
22 }
23}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java
new file mode 100644
index 00000000..b3d05967
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RawPatternMatcher.java
@@ -0,0 +1,15 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
4import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
5import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
6
7public class RawPatternMatcher extends GenericPatternMatcher {
8 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
9 super(specification);
10 }
11
12 IQueryResultProvider getBackend() {
13 return backend;
14 }
15}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ResultSetCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java
index 5e6d1970..c2dcc565 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ResultSetCursor.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalCursor.java
@@ -1,18 +1,17 @@
1package tools.refinery.store.query.viatra.internal.pquery; 1package tools.refinery.store.query.viatra.internal.matcher;
2 2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import tools.refinery.store.map.Cursor; 4import tools.refinery.store.map.Cursor;
5import tools.refinery.store.query.viatra.ViatraTupleLike;
6import tools.refinery.store.tuple.TupleLike; 5import tools.refinery.store.tuple.TupleLike;
7 6
8import java.util.Iterator; 7import java.util.Iterator;
9 8
10class ResultSetCursor implements Cursor<TupleLike, Boolean> { 9class RelationalCursor implements Cursor<TupleLike, Boolean> {
11 private final Iterator<? extends ITuple> tuplesIterator; 10 private final Iterator<? extends ITuple> tuplesIterator;
12 private boolean terminated; 11 private boolean terminated;
13 private TupleLike key; 12 private TupleLike key;
14 13
15 public ResultSetCursor(Iterator<? extends ITuple> tuplesIterator) { 14 public RelationalCursor(Iterator<? extends ITuple> tuplesIterator) {
16 this.tuplesIterator = tuplesIterator; 15 this.tuplesIterator = tuplesIterator;
17 } 16 }
18 17
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java
new file mode 100644
index 00000000..b9bc3f1e
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/RelationalViatraMatcher.java
@@ -0,0 +1,89 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
4import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
5import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
6import org.eclipse.viatra.query.runtime.rete.index.Indexer;
7import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
8import tools.refinery.store.map.Cursor;
9import tools.refinery.store.map.Cursors;
10import tools.refinery.store.query.ModelQueryAdapter;
11import tools.refinery.store.query.ResultSet;
12import tools.refinery.store.query.dnf.Query;
13import tools.refinery.store.query.dnf.RelationalQuery;
14import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
15import tools.refinery.store.tuple.TupleLike;
16
17/**
18 * Directly access the tuples inside a VIATRA pattern matcher.<p>
19 * This class neglects calling
20 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}
21 * and
22 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)},
23 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial
24 * implementation for these methods.
25 * Using this class with any other runtime context may lead to undefined behavior.
26 */
27public class RelationalViatraMatcher implements ResultSet<Boolean> {
28 private final ViatraModelQueryAdapterImpl adapter;
29 private final RelationalQuery query;
30 private final TupleMask emptyMask;
31 private final TupleMask identityMask;
32 private final IQueryResultProvider backend;
33 private final Indexer emptyMaskIndexer;
34
35 public RelationalViatraMatcher(ViatraModelQueryAdapterImpl adapter, RelationalQuery query,
36 RawPatternMatcher rawPatternMatcher) {
37 this.adapter = adapter;
38 this.query = query;
39 int arity = query.arity();
40 emptyMask = TupleMask.empty(arity);
41 identityMask = TupleMask.identity(arity);
42 backend = rawPatternMatcher.getBackend();
43 if (backend instanceof RetePatternMatcher reteBackend) {
44 emptyMaskIndexer = IndexerUtils.getIndexer(reteBackend, emptyMask);
45 } else {
46 emptyMaskIndexer = null;
47 }
48 }
49
50 @Override
51 public ModelQueryAdapter getAdapter() {
52 return adapter;
53 }
54
55 @Override
56 public Query<Boolean> getQuery() {
57 return query;
58 }
59
60 @Override
61 public Boolean get(TupleLike parameters) {
62 var tuple = MatcherUtils.toViatraTuple(parameters);
63 if (emptyMaskIndexer == null) {
64 return backend.hasMatch(identityMask, tuple);
65 }
66 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
67 return matches != null && matches.contains(tuple);
68 }
69
70 @Override
71 public Cursor<TupleLike, Boolean> getAll() {
72 if (emptyMaskIndexer == null) {
73 var allMatches = backend.getAllMatches(emptyMask, Tuples.staticArityFlatTupleOf());
74 return new RelationalCursor(allMatches.iterator());
75 }
76 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
77 return matches == null ? Cursors.empty() : new RelationalCursor(matches.stream().iterator());
78 }
79
80 @Override
81 public int size() {
82 if (emptyMaskIndexer == null) {
83 return backend.countMatches(emptyMask, Tuples.staticArityFlatTupleOf());
84 }
85 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
86 return matches == null ? 0 : matches.size();
87 }
88
89}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java
new file mode 100644
index 00000000..6c53fff1
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/UnsafeFunctionalCursor.java
@@ -0,0 +1,52 @@
1package tools.refinery.store.query.viatra.internal.matcher;
2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import tools.refinery.store.map.Cursor;
5import tools.refinery.store.tuple.TupleLike;
6
7import java.util.Iterator;
8
9/**
10 * Cursor for a functional result set that iterates over a stream of raw matches and doesn't check whether the
11 * functional dependency of the output on the inputs is obeyed.
12 * @param <T> The output type.
13 */
14class UnsafeFunctionalCursor<T> implements Cursor<TupleLike, T> {
15 private final Iterator<? extends ITuple> tuplesIterator;
16 private boolean terminated;
17 private TupleLike key;
18 private T value;
19
20 public UnsafeFunctionalCursor(Iterator<? extends ITuple> tuplesIterator) {
21 this.tuplesIterator = tuplesIterator;
22 }
23
24 @Override
25 public TupleLike getKey() {
26 return key;
27 }
28
29 @Override
30 public T getValue() {
31 return value;
32 }
33
34 @Override
35 public boolean isTerminated() {
36 return terminated;
37 }
38
39 @Override
40 public boolean move() {
41 if (!terminated && tuplesIterator.hasNext()) {
42 var match = tuplesIterator.next();
43 key = new OmitOutputViatraTupleLike(match);
44 @SuppressWarnings("unchecked")
45 var typedValue = (T) match.get(match.getSize() - 1);
46 value = typedValue;
47 return true;
48 }
49 terminated = true;
50 return false;
51 }
52}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraTupleLike.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/ViatraTupleLike.java
index 46c28434..76a3e40b 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/ViatraTupleLike.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/matcher/ViatraTupleLike.java
@@ -1,10 +1,10 @@
1package tools.refinery.store.query.viatra; 1package tools.refinery.store.query.viatra.internal.matcher;
2 2
3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 3import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
4import tools.refinery.store.tuple.Tuple1; 4import tools.refinery.store.tuple.Tuple1;
5import tools.refinery.store.tuple.TupleLike; 5import tools.refinery.store.tuple.TupleLike;
6 6
7public record ViatraTupleLike(ITuple wrappedTuple) implements TupleLike { 7record ViatraTupleLike(ITuple wrappedTuple) implements TupleLike {
8 @Override 8 @Override
9 public int getSize() { 9 public int getSize() {
10 return wrappedTuple.getSize(); 10 return wrappedTuple.getSize();
@@ -15,4 +15,9 @@ public record ViatraTupleLike(ITuple wrappedTuple) implements TupleLike {
15 var wrappedValue = (Tuple1) wrappedTuple.get(element); 15 var wrappedValue = (Tuple1) wrappedTuple.get(element);
16 return wrappedValue.value0(); 16 return wrappedValue.value0();
17 } 17 }
18
19 @Override
20 public String toString() {
21 return TupleLike.toString(this);
22 }
18} 23}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/AssumptionEvaluator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/AssumptionEvaluator.java
new file mode 100644
index 00000000..a80b0f90
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/AssumptionEvaluator.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
4import tools.refinery.store.query.term.Term;
5
6class AssumptionEvaluator extends TermEvaluator<Boolean> {
7 public AssumptionEvaluator(Term<Boolean> term) {
8 super(term);
9 }
10
11 @Override
12 public Object evaluateExpression(IValueProvider provider) {
13 var result = super.evaluateExpression(provider);
14 return result == null ? Boolean.FALSE : result;
15 }
16}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
index 201e0ed0..7afeb977 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/Dnf2PQuery.java
@@ -1,45 +1,44 @@
1package tools.refinery.store.query.viatra.internal.pquery; 1package tools.refinery.store.query.viatra.internal.pquery;
2 2
3import org.eclipse.viatra.query.runtime.matchers.backend.IQueryBackendFactory;
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint; 4import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import org.eclipse.viatra.query.runtime.matchers.context.IInputKey; 5import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
5import org.eclipse.viatra.query.runtime.matchers.psystem.PBody; 6import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
6import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable; 7import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
8import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.BoundAggregator;
9import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
7import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation; 10import org.eclipse.viatra.query.runtime.matchers.psystem.annotations.PAnnotation;
8import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Equality; 11import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.*;
9import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter;
10import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.Inequality;
11import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.NegativePatternCall;
12import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure; 12import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.BinaryTransitiveClosure;
13import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue; 13import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.ConstantValue;
14import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall; 14import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
15import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint; 15import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
16import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; 16import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
17import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; 17import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
18import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 18import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
19import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 19import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
20import tools.refinery.store.query.Dnf; 20import tools.refinery.store.query.dnf.Dnf;
21import tools.refinery.store.query.DnfClause; 21import tools.refinery.store.query.dnf.DnfClause;
22import tools.refinery.store.query.DnfUtils;
23import tools.refinery.store.query.Variable;
24import tools.refinery.store.query.literal.*; 22import tools.refinery.store.query.literal.*;
23import tools.refinery.store.query.term.ConstantTerm;
24import tools.refinery.store.query.term.StatefulAggregator;
25import tools.refinery.store.query.term.StatelessAggregator;
26import tools.refinery.store.query.term.Variable;
25import tools.refinery.store.query.view.AnyRelationView; 27import tools.refinery.store.query.view.AnyRelationView;
26import tools.refinery.store.util.CycleDetectingMapper; 28import tools.refinery.store.util.CycleDetectingMapper;
27 29
28import java.util.*; 30import java.util.*;
29import java.util.function.Function; 31import java.util.function.Function;
32import java.util.stream.Collectors;
30 33
31public class Dnf2PQuery { 34public class Dnf2PQuery {
32 private static final Object P_CONSTRAINT_LOCK = new Object(); 35 private static final Object P_CONSTRAINT_LOCK = new Object();
33
34 private final CycleDetectingMapper<Dnf, RawPQuery> mapper = new CycleDetectingMapper<>(Dnf::name, 36 private final CycleDetectingMapper<Dnf, RawPQuery> mapper = new CycleDetectingMapper<>(Dnf::name,
35 this::doTranslate); 37 this::doTranslate);
36 38 private final QueryWrapperFactory wrapperFactory = new QueryWrapperFactory(this);
37 private final Map<AnyRelationView, RelationViewWrapper> view2WrapperMap = new LinkedHashMap<>(); 39 private final Map<Dnf, QueryEvaluationHint> hintOverrides = new LinkedHashMap<>();
38
39 private final Map<AnyRelationView, RawPQuery> view2EmbeddedMap = new HashMap<>();
40
41 private Function<Dnf, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null, 40 private Function<Dnf, QueryEvaluationHint> computeHint = dnf -> new QueryEvaluationHint(null,
42 QueryEvaluationHint.BackendRequirement.UNSPECIFIED); 41 (IQueryBackendFactory) null);
43 42
44 public void setComputeHint(Function<Dnf, QueryEvaluationHint> computeHint) { 43 public void setComputeHint(Function<Dnf, QueryEvaluationHint> computeHint) {
45 this.computeHint = computeHint; 44 this.computeHint = computeHint;
@@ -50,16 +49,33 @@ public class Dnf2PQuery {
50 } 49 }
51 50
52 public Map<AnyRelationView, IInputKey> getRelationViews() { 51 public Map<AnyRelationView, IInputKey> getRelationViews() {
53 return Collections.unmodifiableMap(view2WrapperMap); 52 return wrapperFactory.getRelationViews();
54 } 53 }
55 54
56 public RawPQuery getAlreadyTranslated(Dnf dnfQuery) { 55 public void hint(Dnf dnf, QueryEvaluationHint hint) {
57 return mapper.getAlreadyMapped(dnfQuery); 56 hintOverrides.compute(dnf, (ignoredKey, existingHint) ->
57 existingHint == null ? hint : existingHint.overrideBy(hint));
58 }
59
60 private QueryEvaluationHint consumeHint(Dnf dnf) {
61 var defaultHint = computeHint.apply(dnf);
62 var existingHint = hintOverrides.remove(dnf);
63 return defaultHint.overrideBy(existingHint);
64 }
65
66 public void assertNoUnusedHints() {
67 if (hintOverrides.isEmpty()) {
68 return;
69 }
70 var unusedHints = hintOverrides.keySet().stream().map(Dnf::name).collect(Collectors.joining(", "));
71 throw new IllegalStateException(
72 "Unused query evaluation hints for %s. Hints must be set before a query is added to the engine"
73 .formatted(unusedHints));
58 } 74 }
59 75
60 private RawPQuery doTranslate(Dnf dnfQuery) { 76 private RawPQuery doTranslate(Dnf dnfQuery) {
61 var pQuery = new RawPQuery(dnfQuery.getUniqueName()); 77 var pQuery = new RawPQuery(dnfQuery.getUniqueName());
62 pQuery.setEvaluationHints(computeHint.apply(dnfQuery)); 78 pQuery.setEvaluationHints(consumeHint(dnfQuery));
63 79
64 Map<Variable, PParameter> parameters = new HashMap<>(); 80 Map<Variable, PParameter> parameters = new HashMap<>();
65 for (Variable variable : dnfQuery.getParameters()) { 81 for (Variable variable : dnfQuery.getParameters()) {
@@ -97,7 +113,7 @@ public class Dnf2PQuery {
97 body.setSymbolicParameters(symbolicParameters); 113 body.setSymbolicParameters(symbolicParameters);
98 pQuery.addBody(body); 114 pQuery.addBody(body);
99 for (Literal literal : clause.literals()) { 115 for (Literal literal : clause.literals()) {
100 translateLiteral(literal, body); 116 translateLiteral(literal, clause, body);
101 } 117 }
102 } 118 }
103 } 119 }
@@ -105,15 +121,21 @@ public class Dnf2PQuery {
105 return pQuery; 121 return pQuery;
106 } 122 }
107 123
108 private void translateLiteral(Literal literal, PBody body) { 124 private void translateLiteral(Literal literal, DnfClause clause, PBody body) {
109 if (literal instanceof EquivalenceLiteral equivalenceLiteral) { 125 if (literal instanceof EquivalenceLiteral equivalenceLiteral) {
110 translateEquivalenceLiteral(equivalenceLiteral, body); 126 translateEquivalenceLiteral(equivalenceLiteral, body);
111 } else if (literal instanceof RelationViewLiteral relationViewLiteral) { 127 } else if (literal instanceof CallLiteral callLiteral) {
112 translateRelationViewLiteral(relationViewLiteral, body); 128 translateCallLiteral(callLiteral, clause, body);
113 } else if (literal instanceof DnfCallLiteral dnfCallLiteral) {
114 translateDnfCallLiteral(dnfCallLiteral, body);
115 } else if (literal instanceof ConstantLiteral constantLiteral) { 129 } else if (literal instanceof ConstantLiteral constantLiteral) {
116 translateConstantLiteral(constantLiteral, body); 130 translateConstantLiteral(constantLiteral, body);
131 } else if (literal instanceof AssignLiteral<?> assignLiteral) {
132 translateAssignLiteral(assignLiteral, body);
133 } else if (literal instanceof AssumeLiteral assumeLiteral) {
134 translateAssumeLiteral(assumeLiteral, body);
135 } else if (literal instanceof CountLiteral countLiteral) {
136 translateCountLiteral(countLiteral, clause, body);
137 } else if (literal instanceof AggregationLiteral<?, ?> aggregationLiteral) {
138 translateAggregationLiteral(aggregationLiteral, clause, body);
117 } else { 139 } else {
118 throw new IllegalArgumentException("Unknown literal: " + literal.toString()); 140 throw new IllegalArgumentException("Unknown literal: " + literal.toString());
119 } 141 }
@@ -129,20 +151,43 @@ public class Dnf2PQuery {
129 } 151 }
130 } 152 }
131 153
132 private void translateRelationViewLiteral(RelationViewLiteral relationViewLiteral, PBody body) { 154 private void translateCallLiteral(CallLiteral callLiteral, DnfClause clause, PBody body) {
133 var substitution = translateSubstitution(relationViewLiteral.getArguments(), body); 155 var polarity = callLiteral.getPolarity();
134 var polarity = relationViewLiteral.getPolarity(); 156 switch (polarity) {
135 var relationView = relationViewLiteral.getTarget(); 157 case POSITIVE -> {
136 if (polarity == CallPolarity.POSITIVE) { 158 var substitution = translateSubstitution(callLiteral.getArguments(), body);
137 new TypeConstraint(body, substitution, wrapView(relationView)); 159 var constraint = callLiteral.getTarget();
138 } else { 160 if (constraint instanceof Dnf dnf) {
139 var embeddedPQuery = translateEmbeddedRelationViewPQuery(relationView); 161 var pattern = translate(dnf);
140 switch (polarity) { 162 new PositivePatternCall(body, substitution, pattern);
141 case TRANSITIVE -> new BinaryTransitiveClosure(body, substitution, embeddedPQuery); 163 } else if (constraint instanceof AnyRelationView relationView) {
142 case NEGATIVE -> new NegativePatternCall(body, substitution, embeddedPQuery); 164 var inputKey = wrapperFactory.getInputKey(relationView);
143 default -> throw new IllegalArgumentException("Unknown polarity: " + polarity); 165 new TypeConstraint(body, substitution, inputKey);
166 } else {
167 throw new IllegalArgumentException("Unknown Constraint: " + constraint);
144 } 168 }
145 } 169 }
170 case TRANSITIVE -> {
171 var substitution = translateSubstitution(callLiteral.getArguments(), body);
172 var constraint = callLiteral.getTarget();
173 PQuery pattern;
174 if (constraint instanceof Dnf dnf) {
175 pattern = translate(dnf);
176 } else if (constraint instanceof AnyRelationView relationView) {
177 pattern = wrapperFactory.wrapRelationViewIdentityArguments(relationView);
178 } else {
179 throw new IllegalArgumentException("Unknown Constraint: " + constraint);
180 }
181 new BinaryTransitiveClosure(body, substitution, pattern);
182 }
183 case NEGATIVE -> {
184 var wrappedCall = wrapperFactory.maybeWrapConstraint(callLiteral, clause);
185 var substitution = translateSubstitution(wrappedCall.remappedArguments(), body);
186 var pattern = wrappedCall.pattern();
187 new NegativePatternCall(body, substitution, pattern);
188 }
189 default -> throw new IllegalArgumentException("Unknown polarity: " + polarity);
190 }
146 } 191 }
147 192
148 private static Tuple translateSubstitution(List<Variable> substitution, PBody body) { 193 private static Tuple translateSubstitution(List<Variable> substitution, PBody body) {
@@ -155,51 +200,57 @@ public class Dnf2PQuery {
155 return Tuples.flatTupleOf(variables); 200 return Tuples.flatTupleOf(variables);
156 } 201 }
157 202
158 private RawPQuery translateEmbeddedRelationViewPQuery(AnyRelationView relationView) { 203 private void translateConstantLiteral(ConstantLiteral constantLiteral, PBody body) {
159 return view2EmbeddedMap.computeIfAbsent(relationView, this::doTranslateEmbeddedRelationViewPQuery); 204 var variable = body.getOrCreateVariableByName(constantLiteral.variable().getUniqueName());
205 new ConstantValue(body, variable, constantLiteral.nodeId());
160 } 206 }
161 207
162 private RawPQuery doTranslateEmbeddedRelationViewPQuery(AnyRelationView relationView) { 208 private <T> void translateAssignLiteral(AssignLiteral<T> assignLiteral, PBody body) {
163 var embeddedPQuery = new RawPQuery(DnfUtils.generateUniqueName(relationView.name()), PVisibility.EMBEDDED); 209 var variable = body.getOrCreateVariableByName(assignLiteral.variable().getUniqueName());
164 var body = new PBody(embeddedPQuery); 210 var term = assignLiteral.term();
165 int arity = relationView.arity(); 211 if (term instanceof ConstantTerm<T> constantTerm) {
166 var parameters = new ArrayList<PParameter>(arity); 212 new ConstantValue(body, variable, constantTerm.getValue());
167 var arguments = new Object[arity]; 213 } else {
168 var symbolicParameters = new ArrayList<ExportedParameter>(arity); 214 var evaluator = new TermEvaluator<>(term);
169 for (int i = 0; i < arity; i++) { 215 new ExpressionEvaluation(body, evaluator, variable);
170 var parameterName = "p" + i;
171 var parameter = new PParameter(parameterName);
172 parameters.add(parameter);
173 var variable = body.getOrCreateVariableByName(parameterName);
174 arguments[i] = variable;
175 symbolicParameters.add(new ExportedParameter(body, variable, parameter));
176 } 216 }
177 embeddedPQuery.setParameters(parameters);
178 body.setSymbolicParameters(symbolicParameters);
179 var argumentTuple = Tuples.flatTupleOf(arguments);
180 new TypeConstraint(body, argumentTuple, wrapView(relationView));
181 embeddedPQuery.addBody(body);
182 return embeddedPQuery;
183 } 217 }
184 218
185 private RelationViewWrapper wrapView(AnyRelationView relationView) { 219 private void translateAssumeLiteral(AssumeLiteral assumeLiteral, PBody body) {
186 return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new); 220 var evaluator = new AssumptionEvaluator(assumeLiteral.term());
221 new ExpressionEvaluation(body, evaluator, null);
187 } 222 }
188 223
189 private void translateDnfCallLiteral(DnfCallLiteral dnfCallLiteral, PBody body) { 224 private void translateCountLiteral(CountLiteral countLiteral, DnfClause clause, PBody body) {
190 var variablesTuple = translateSubstitution(dnfCallLiteral.getArguments(), body); 225 var wrappedCall = wrapperFactory.maybeWrapConstraint(countLiteral, clause);
191 var translatedReferred = translate(dnfCallLiteral.getTarget()); 226 var substitution = translateSubstitution(wrappedCall.remappedArguments(), body);
192 var polarity = dnfCallLiteral.getPolarity(); 227 var resultVariable = body.getOrCreateVariableByName(countLiteral.getResultVariable().getUniqueName());
193 switch (polarity) { 228 new PatternMatchCounter(body, substitution, wrappedCall.pattern(), resultVariable);
194 case POSITIVE -> new PositivePatternCall(body, variablesTuple, translatedReferred);
195 case TRANSITIVE -> new BinaryTransitiveClosure(body, variablesTuple, translatedReferred);
196 case NEGATIVE -> new NegativePatternCall(body, variablesTuple, translatedReferred);
197 default -> throw new IllegalArgumentException("Unknown polarity: " + polarity);
198 }
199 } 229 }
200 230
201 private void translateConstantLiteral(ConstantLiteral constantLiteral, PBody body) { 231 private <R, T> void translateAggregationLiteral(AggregationLiteral<R, T> aggregationLiteral, DnfClause clause,
202 var variable = body.getOrCreateVariableByName(constantLiteral.variable().getUniqueName()); 232 PBody body) {
203 new ConstantValue(body, variable, constantLiteral.nodeId()); 233 var aggregator = aggregationLiteral.getAggregator();
234 IMultisetAggregationOperator<T, ?, R> aggregationOperator;
235 if (aggregator instanceof StatelessAggregator<R, T> statelessAggregator) {
236 aggregationOperator = new StatelessMultisetAggregator<>(statelessAggregator);
237 } else if (aggregator instanceof StatefulAggregator<R, T> statefulAggregator) {
238 aggregationOperator = new StatefulMultisetAggregator<>(statefulAggregator);
239 } else {
240 throw new IllegalArgumentException("Unknown aggregator: " + aggregator);
241 }
242 var wrappedCall = wrapperFactory.maybeWrapConstraint(aggregationLiteral, clause);
243 var substitution = translateSubstitution(wrappedCall.remappedArguments(), body);
244 var inputVariable = body.getOrCreateVariableByName(aggregationLiteral.getInputVariable().getUniqueName());
245 var aggregatedColumn = substitution.invertIndex().get(inputVariable);
246 if (aggregatedColumn == null) {
247 throw new IllegalStateException("Input variable %s not found in substitution %s".formatted(inputVariable,
248 substitution));
249 }
250 var boundAggregator = new BoundAggregator(aggregationOperator, aggregator.getInputType(),
251 aggregator.getResultType());
252 var resultVariable = body.getOrCreateVariableByName(aggregationLiteral.getResultVariable().getUniqueName());
253 new AggregatorConstraint(boundAggregator, body, substitution, wrappedCall.pattern(), resultVariable,
254 aggregatedColumn);
204 } 255 }
205} 256}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java
new file mode 100644
index 00000000..24ae5196
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/QueryWrapperFactory.java
@@ -0,0 +1,173 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.psystem.PBody;
5import org.eclipse.viatra.query.runtime.matchers.psystem.PVariable;
6import org.eclipse.viatra.query.runtime.matchers.psystem.basicdeferred.ExportedParameter;
7import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.PositivePatternCall;
8import org.eclipse.viatra.query.runtime.matchers.psystem.basicenumerables.TypeConstraint;
9import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PQuery;
11import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
12import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
13import tools.refinery.store.query.Constraint;
14import tools.refinery.store.query.dnf.Dnf;
15import tools.refinery.store.query.dnf.DnfClause;
16import tools.refinery.store.query.dnf.DnfUtils;
17import tools.refinery.store.query.literal.AbstractCallLiteral;
18import tools.refinery.store.query.term.Variable;
19import tools.refinery.store.query.view.AnyRelationView;
20import tools.refinery.store.query.view.RelationView;
21import tools.refinery.store.util.CycleDetectingMapper;
22
23import java.util.*;
24import java.util.function.ToIntFunction;
25
26class QueryWrapperFactory {
27 private final Dnf2PQuery dnf2PQuery;
28 private final Map<AnyRelationView, RelationViewWrapper> view2WrapperMap = new LinkedHashMap<>();
29 private final CycleDetectingMapper<RemappedConstraint, RawPQuery> wrapConstraint = new CycleDetectingMapper<>(
30 RemappedConstraint::toString, this::doWrapConstraint);
31
32 QueryWrapperFactory(Dnf2PQuery dnf2PQuery) {
33 this.dnf2PQuery = dnf2PQuery;
34 }
35
36 public PQuery wrapRelationViewIdentityArguments(AnyRelationView relationView) {
37 var identity = new int[relationView.arity()];
38 for (int i = 0; i < identity.length; i++) {
39 identity[i] = i;
40 }
41 return maybeWrapConstraint(relationView, identity);
42 }
43 public WrappedCall maybeWrapConstraint(AbstractCallLiteral callLiteral, DnfClause clause) {
44 var arguments = callLiteral.getArguments();
45 int arity = arguments.size();
46 var remappedParameters = new int[arity];
47 var boundVariables = clause.boundVariables();
48 var unboundVariableIndices = new HashMap<Variable, Integer>();
49 var appendVariable = new VariableAppender();
50 for (int i = 0; i < arity; i++) {
51 var variable = arguments.get(i);
52 if (boundVariables.contains(variable)) {
53 // Do not join bound variable to make sure that the embedded pattern stays as general as possible.
54 remappedParameters[i] = appendVariable.applyAsInt(variable);
55 } else {
56 remappedParameters[i] = unboundVariableIndices.computeIfAbsent(variable, appendVariable::applyAsInt);
57 }
58 }
59 var pattern = maybeWrapConstraint(callLiteral.getTarget(), remappedParameters);
60 return new WrappedCall(pattern, appendVariable.getRemappedArguments());
61 }
62
63 private PQuery maybeWrapConstraint(Constraint constraint, int[] remappedParameters) {
64 if (remappedParameters.length != constraint.arity()) {
65 throw new IllegalArgumentException("Constraint %s expected %d parameters, but got %d parameters".formatted(
66 constraint, constraint.arity(), remappedParameters.length));
67 }
68 if (constraint instanceof Dnf dnf && isIdentity(remappedParameters)) {
69 return dnf2PQuery.translate(dnf);
70 }
71 return wrapConstraint.map(new RemappedConstraint(constraint, remappedParameters));
72 }
73
74 private static boolean isIdentity(int[] remappedParameters) {
75 for (int i = 0; i < remappedParameters.length; i++) {
76 if (remappedParameters[i] != i) {
77 return false;
78 }
79 }
80 return true;
81 }
82
83 private RawPQuery doWrapConstraint(RemappedConstraint remappedConstraint) {
84 var constraint = remappedConstraint.constraint();
85 var remappedParameters = remappedConstraint.remappedParameters();
86
87 var embeddedPQuery = new RawPQuery(DnfUtils.generateUniqueName(constraint.name()), PVisibility.EMBEDDED);
88 var body = new PBody(embeddedPQuery);
89 int arity = Arrays.stream(remappedParameters).max().orElse(-1) + 1;
90 var parameters = new ArrayList<PParameter>(arity);
91 var parameterVariables = new PVariable[arity];
92 var symbolicParameters = new ArrayList<ExportedParameter>(arity);
93 for (int i = 0; i < arity; i++) {
94 var parameterName = "p" + i;
95 var parameter = new PParameter(parameterName);
96 parameters.add(parameter);
97 var variable = body.getOrCreateVariableByName(parameterName);
98 parameterVariables[i] = variable;
99 symbolicParameters.add(new ExportedParameter(body, variable, parameter));
100 }
101 embeddedPQuery.setParameters(parameters);
102 body.setSymbolicParameters(symbolicParameters);
103
104 var arguments = new Object[remappedParameters.length];
105 for (int i = 0; i < remappedParameters.length; i++) {
106 arguments[i] = parameterVariables[remappedParameters[i]];
107 }
108 var argumentTuple = Tuples.flatTupleOf(arguments);
109
110 if (constraint instanceof RelationView<?> relationView) {
111 new TypeConstraint(body, argumentTuple, getInputKey(relationView));
112 } else if (constraint instanceof Dnf dnf) {
113 var calledPQuery = dnf2PQuery.translate(dnf);
114 new PositivePatternCall(body, argumentTuple, calledPQuery);
115 } else {
116 throw new IllegalArgumentException("Unknown Constraint: " + constraint);
117 }
118
119 embeddedPQuery.addBody(body);
120 return embeddedPQuery;
121 }
122
123 public IInputKey getInputKey(AnyRelationView relationView) {
124 return view2WrapperMap.computeIfAbsent(relationView, RelationViewWrapper::new);
125 }
126
127 public Map<AnyRelationView, IInputKey> getRelationViews() {
128 return Collections.unmodifiableMap(view2WrapperMap);
129 }
130
131 public record WrappedCall(PQuery pattern, List<Variable> remappedArguments) {
132 }
133
134 private static class VariableAppender implements ToIntFunction<Variable> {
135 private final List<Variable> remappedArguments = new ArrayList<>();
136 private int nextIndex = 0;
137
138 @Override
139 public int applyAsInt(Variable variable) {
140 remappedArguments.add(variable);
141 int index = nextIndex;
142 nextIndex++;
143 return index;
144 }
145
146 public List<Variable> getRemappedArguments() {
147 return remappedArguments;
148 }
149 }
150
151 private record RemappedConstraint(Constraint constraint, int[] remappedParameters) {
152 @Override
153 public boolean equals(Object o) {
154 if (this == o) return true;
155 if (o == null || getClass() != o.getClass()) return false;
156 RemappedConstraint that = (RemappedConstraint) o;
157 return constraint.equals(that.constraint) && Arrays.equals(remappedParameters, that.remappedParameters);
158 }
159
160 @Override
161 public int hashCode() {
162 int result = Objects.hash(constraint);
163 result = 31 * result + Arrays.hashCode(remappedParameters);
164 return result;
165 }
166
167 @Override
168 public String toString() {
169 return "RemappedConstraint{constraint=%s, remappedParameters=%s}".formatted(constraint,
170 Arrays.toString(remappedParameters));
171 }
172 }
173}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
index 71b74396..aad4ba3c 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPQuery.java
@@ -9,6 +9,7 @@ import org.eclipse.viatra.query.runtime.matchers.psystem.queries.BasePQuery;
9import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter; 9import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PParameter;
10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility; 10import org.eclipse.viatra.query.runtime.matchers.psystem.queries.PVisibility;
11import tools.refinery.store.query.viatra.internal.RelationalScope; 11import tools.refinery.store.query.viatra.internal.RelationalScope;
12import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher;
12 13
13import java.util.LinkedHashSet; 14import java.util.LinkedHashSet;
14import java.util.List; 15import java.util.List;
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java
deleted file mode 100644
index 5924ff15..00000000
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RawPatternMatcher.java
+++ /dev/null
@@ -1,93 +0,0 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.api.GenericPatternMatcher;
4import org.eclipse.viatra.query.runtime.api.GenericQuerySpecification;
5import org.eclipse.viatra.query.runtime.api.ViatraQueryEngine;
6import org.eclipse.viatra.query.runtime.matchers.backend.IMatcherCapability;
7import org.eclipse.viatra.query.runtime.matchers.backend.IQueryResultProvider;
8import org.eclipse.viatra.query.runtime.matchers.tuple.TupleMask;
9import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
10import org.eclipse.viatra.query.runtime.rete.index.Indexer;
11import org.eclipse.viatra.query.runtime.rete.matcher.RetePatternMatcher;
12import tools.refinery.store.map.Cursor;
13import tools.refinery.store.map.Cursors;
14import tools.refinery.store.query.ResultSet;
15import tools.refinery.store.query.viatra.ViatraTupleLike;
16import tools.refinery.store.tuple.Tuple;
17import tools.refinery.store.tuple.TupleLike;
18
19/**
20 * Directly access the tuples inside a VIATRA pattern matcher.<p>
21 * This class neglects calling
22 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#wrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)}
23 * and
24 * {@link org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContext#unwrapTuple(org.eclipse.viatra.query.runtime.matchers.tuple.Tuple)},
25 * because {@link tools.refinery.store.query.viatra.internal.context.RelationalRuntimeContext} provides a trivial
26 * implementation for these methods.
27 * Using this class with any other runtime context may lead to undefined behavior.
28 */
29public class RawPatternMatcher extends GenericPatternMatcher implements ResultSet {
30 private final Object[] empty;
31 private final TupleMask identityMask;
32 private Indexer emptyMaskIndexer;
33
34 public RawPatternMatcher(GenericQuerySpecification<? extends GenericPatternMatcher> specification) {
35 super(specification);
36 var arity = specification.getParameterNames().size();
37 empty = new Object[arity];
38 identityMask = TupleMask.identity(arity);
39 }
40
41 @Override
42 protected void setBackend(ViatraQueryEngine engine, IQueryResultProvider resultProvider,
43 IMatcherCapability capabilities) {
44 super.setBackend(engine, resultProvider, capabilities);
45 if (resultProvider instanceof RetePatternMatcher reteBackend) {
46 emptyMaskIndexer = IndexerUtils.getIndexer(reteBackend, TupleMask.empty(identityMask.sourceWidth));
47 }
48 }
49
50 @Override
51 public boolean hasResult(TupleLike parameters) {
52 org.eclipse.viatra.query.runtime.matchers.tuple.Tuple tuple;
53 if (parameters instanceof ViatraTupleLike viatraTupleLike) {
54 tuple = viatraTupleLike.wrappedTuple().toImmutable();
55 } else {
56 var parametersArray = toParametersArray(parameters);
57 tuple = Tuples.flatTupleOf(parametersArray);
58 }
59 if (emptyMaskIndexer == null) {
60 return backend.hasMatch(identityMask, tuple);
61 }
62 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
63 return matches != null && matches.contains(tuple);
64 }
65
66 @Override
67 public Cursor<TupleLike, Boolean> allResults() {
68 if (emptyMaskIndexer == null) {
69 return new ResultSetCursor(backend.getAllMatches(empty).iterator());
70 }
71 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
72 return matches == null ? Cursors.empty() : new ResultSetCursor(matches.stream().iterator());
73 }
74
75 @Override
76 public int countResults() {
77 if (emptyMaskIndexer == null) {
78 return backend.countMatches(empty);
79 }
80 var matches = emptyMaskIndexer.get(Tuples.staticArityFlatTupleOf());
81 return matches == null ? 0 : matches.size();
82 }
83
84 private Object[] toParametersArray(TupleLike tuple) {
85 int size = tuple.getSize();
86 var array = new Object[size];
87 for (int i = 0; i < size; i++) {
88 var value = tuple.get(i);
89 array[i] = Tuple.of(value);
90 }
91 return array;
92 }
93}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java
index c442add8..48bf558d 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/RelationViewWrapper.java
@@ -27,4 +27,9 @@ public class RelationViewWrapper extends BaseInputKeyWrapper<AnyRelationView> {
27 public boolean isEnumerable() { 27 public boolean isEnumerable() {
28 return true; 28 return true;
29 } 29 }
30
31 @Override
32 public String toString() {
33 return "RelationViewWrapper{wrappedKey=%s}".formatted(wrappedKey);
34 }
30} 35}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java
new file mode 100644
index 00000000..2798a252
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatefulMultisetAggregator.java
@@ -0,0 +1,60 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
4import tools.refinery.store.query.term.StatefulAggregate;
5import tools.refinery.store.query.term.StatefulAggregator;
6
7import java.util.stream.Stream;
8
9record StatefulMultisetAggregator<R, T>(StatefulAggregator<R, T> aggregator)
10 implements IMultisetAggregationOperator<T, StatefulAggregate<R, T>, R> {
11 @Override
12 public String getShortDescription() {
13 return getName();
14 }
15
16 @Override
17 public String getName() {
18 return aggregator.toString();
19 }
20
21 @Override
22 public StatefulAggregate<R, T> createNeutral() {
23 return aggregator.createEmptyAggregate();
24 }
25
26 @Override
27 public boolean isNeutral(StatefulAggregate<R, T> result) {
28 return result.isEmpty();
29 }
30
31 @Override
32 public StatefulAggregate<R, T> update(StatefulAggregate<R, T> oldResult, T updateValue, boolean isInsertion) {
33 if (isInsertion) {
34 oldResult.add(updateValue);
35 } else {
36 oldResult.remove(updateValue);
37 }
38 return oldResult;
39 }
40
41 @Override
42 public R getAggregate(StatefulAggregate<R, T> result) {
43 return result.getResult();
44 }
45
46 @Override
47 public R aggregateStream(Stream<T> stream) {
48 return aggregator.aggregateStream(stream);
49 }
50
51 @Override
52 public StatefulAggregate<R, T> clone(StatefulAggregate<R, T> original) {
53 return original.deepCopy();
54 }
55
56 @Override
57 public boolean contains(T value, StatefulAggregate<R, T> accumulator) {
58 return accumulator.contains(value);
59 }
60}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java
new file mode 100644
index 00000000..7cc71ee9
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/StatelessMultisetAggregator.java
@@ -0,0 +1,50 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator;
4import tools.refinery.store.query.term.StatelessAggregator;
5
6import java.util.stream.Stream;
7
8record StatelessMultisetAggregator<R, T>(StatelessAggregator<R, T> aggregator)
9 implements IMultisetAggregationOperator<T, R, R> {
10 @Override
11 public String getShortDescription() {
12 return getName();
13 }
14
15 @Override
16 public String getName() {
17 return aggregator.toString();
18 }
19
20 @Override
21 public R createNeutral() {
22 return aggregator.getEmptyResult();
23 }
24
25 @Override
26 public boolean isNeutral(R result) {
27 return createNeutral().equals(result);
28 }
29
30 @Override
31 public R update(R oldResult, T updateValue, boolean isInsertion) {
32 return isInsertion ? aggregator.add(oldResult, updateValue) : aggregator.remove(oldResult, updateValue);
33 }
34
35 @Override
36 public R getAggregate(R result) {
37 return result;
38 }
39
40 @Override
41 public R clone(R original) {
42 // Aggregate result is immutable.
43 return original;
44 }
45
46 @Override
47 public R aggregateStream(Stream<T> stream) {
48 return aggregator.aggregateStream(stream);
49 }
50}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java
new file mode 100644
index 00000000..ab123c50
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/TermEvaluator.java
@@ -0,0 +1,32 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.IExpressionEvaluator;
4import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
5import tools.refinery.store.query.term.Term;
6import tools.refinery.store.query.term.Variable;
7
8import java.util.stream.Collectors;
9
10class TermEvaluator<T> implements IExpressionEvaluator {
11 private final Term<T> term;
12
13 public TermEvaluator(Term<T> term) {
14 this.term = term;
15 }
16
17 @Override
18 public String getShortDescription() {
19 return term.toString();
20 }
21
22 @Override
23 public Iterable<String> getInputParameterNames() {
24 return term.getInputVariables().stream().map(Variable::getUniqueName).collect(Collectors.toUnmodifiableSet());
25 }
26
27 @Override
28 public Object evaluateExpression(IValueProvider provider) {
29 var valuation = new ValueProviderBasedValuation(provider);
30 return term.evaluate(valuation);
31 }
32}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java
new file mode 100644
index 00000000..30c2fce7
--- /dev/null
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/pquery/ValueProviderBasedValuation.java
@@ -0,0 +1,14 @@
1package tools.refinery.store.query.viatra.internal.pquery;
2
3import org.eclipse.viatra.query.runtime.matchers.psystem.IValueProvider;
4import tools.refinery.store.query.term.DataVariable;
5import tools.refinery.store.query.valuation.Valuation;
6
7public record ValueProviderBasedValuation(IValueProvider valueProvider) implements Valuation {
8 @Override
9 public <T> T getValue(DataVariable<T> variable) {
10 @SuppressWarnings("unchecked")
11 var value = (T) valueProvider.getValue(variable.getUniqueName());
12 return value;
13 }
14}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
index 8a467066..fc935aa6 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/ModelUpdateListener.java
@@ -22,10 +22,9 @@ public class ModelUpdateListener {
22 } 22 }
23 23
24 private <T> void registerView(ViatraModelQueryAdapterImpl adapter, RelationView<T> relationView) { 24 private <T> void registerView(ViatraModelQueryAdapterImpl adapter, RelationView<T> relationView) {
25 var listener = RelationViewUpdateListener.of(adapter, relationView);
26 var model = adapter.getModel(); 25 var model = adapter.getModel();
27 var interpretation = model.getInterpretation(relationView.getSymbol()); 26 var interpretation = model.getInterpretation(relationView.getSymbol());
28 interpretation.addListener(listener, true); 27 var listener = RelationViewUpdateListener.of(adapter, relationView, interpretation);
29 relationViewUpdateListeners.put(relationView, listener); 28 relationViewUpdateListeners.put(relationView, listener);
30 } 29 }
31 30
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java
index bf6b4197..5e5f60e3 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/RelationViewUpdateListener.java
@@ -4,6 +4,7 @@ import org.eclipse.viatra.query.runtime.matchers.context.IInputKey;
4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener; 4import org.eclipse.viatra.query.runtime.matchers.context.IQueryRuntimeContextListener;
5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple; 5import org.eclipse.viatra.query.runtime.matchers.tuple.ITuple;
6import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple; 6import org.eclipse.viatra.query.runtime.matchers.tuple.Tuple;
7import tools.refinery.store.model.Interpretation;
7import tools.refinery.store.model.InterpretationListener; 8import tools.refinery.store.model.InterpretationListener;
8import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 9import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
9import tools.refinery.store.query.view.RelationView; 10import tools.refinery.store.query.view.RelationView;
@@ -14,18 +15,27 @@ import java.util.List;
14 15
15public abstract class RelationViewUpdateListener<T> implements InterpretationListener<T> { 16public abstract class RelationViewUpdateListener<T> implements InterpretationListener<T> {
16 private final ViatraModelQueryAdapterImpl adapter; 17 private final ViatraModelQueryAdapterImpl adapter;
18 private final Interpretation<T> interpretation;
17 private final List<RelationViewFilter> filters = new ArrayList<>(); 19 private final List<RelationViewFilter> filters = new ArrayList<>();
18 20
19 protected RelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter) { 21 protected RelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter, Interpretation<T> interpretation) {
20 this.adapter = adapter; 22 this.adapter = adapter;
23 this.interpretation = interpretation;
21 } 24 }
22 25
23 public void addFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) { 26 public void addFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
27 if (filters.isEmpty()) {
28 // First filter to be added, from now on we have to subscribe to model updates.
29 interpretation.addListener(this, true);
30 }
24 filters.add(new RelationViewFilter(inputKey, seed, listener)); 31 filters.add(new RelationViewFilter(inputKey, seed, listener));
25 } 32 }
26 33
27 public void removeFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) { 34 public void removeFilter(IInputKey inputKey, ITuple seed, IQueryRuntimeContextListener listener) {
28 filters.remove(new RelationViewFilter(inputKey, seed, listener)); 35 if (filters.remove(new RelationViewFilter(inputKey, seed, listener)) && filters.isEmpty()) {
36 // Last listener to be added, we don't have be subscribed to model updates anymore.
37 interpretation.removeListener(this);
38 }
29 } 39 }
30 40
31 protected void processUpdate(Tuple tuple, boolean isInsertion) { 41 protected void processUpdate(Tuple tuple, boolean isInsertion) {
@@ -39,10 +49,12 @@ public abstract class RelationViewUpdateListener<T> implements InterpretationLis
39 } 49 }
40 50
41 public static <T> RelationViewUpdateListener<T> of(ViatraModelQueryAdapterImpl adapter, 51 public static <T> RelationViewUpdateListener<T> of(ViatraModelQueryAdapterImpl adapter,
42 RelationView<T> relationView) { 52 RelationView<T> relationView,
53 Interpretation<T> interpretation) {
43 if (relationView instanceof TuplePreservingRelationView<T> tuplePreservingRelationView) { 54 if (relationView instanceof TuplePreservingRelationView<T> tuplePreservingRelationView) {
44 return new TuplePreservingRelationViewUpdateListener<>(adapter, tuplePreservingRelationView); 55 return new TuplePreservingRelationViewUpdateListener<>(adapter, tuplePreservingRelationView,
56 interpretation);
45 } 57 }
46 return new TupleChangingRelationViewUpdateListener<>(adapter, relationView); 58 return new TupleChangingRelationViewUpdateListener<>(adapter, relationView, interpretation);
47 } 59 }
48} 60}
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java
index 14142884..0f6ed3b3 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TupleChangingRelationViewUpdateListener.java
@@ -1,6 +1,7 @@
1package tools.refinery.store.query.viatra.internal.update; 1package tools.refinery.store.query.viatra.internal.update;
2 2
3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
4import tools.refinery.store.model.Interpretation;
4import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 5import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
5import tools.refinery.store.query.view.RelationView; 6import tools.refinery.store.query.view.RelationView;
6import tools.refinery.store.tuple.Tuple; 7import tools.refinery.store.tuple.Tuple;
@@ -10,8 +11,9 @@ import java.util.Arrays;
10public class TupleChangingRelationViewUpdateListener<T> extends RelationViewUpdateListener<T> { 11public class TupleChangingRelationViewUpdateListener<T> extends RelationViewUpdateListener<T> {
11 private final RelationView<T> relationView; 12 private final RelationView<T> relationView;
12 13
13 TupleChangingRelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter, RelationView<T> relationView) { 14 TupleChangingRelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter, RelationView<T> relationView,
14 super(adapter); 15 Interpretation<T> interpretation) {
16 super(adapter, interpretation);
15 this.relationView = relationView; 17 this.relationView = relationView;
16 } 18 }
17 19
diff --git a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java
index 288e018a..91e9371b 100644
--- a/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java
+++ b/subprojects/store-query-viatra/src/main/java/tools/refinery/store/query/viatra/internal/update/TuplePreservingRelationViewUpdateListener.java
@@ -1,6 +1,7 @@
1package tools.refinery.store.query.viatra.internal.update; 1package tools.refinery.store.query.viatra.internal.update;
2 2
3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples; 3import org.eclipse.viatra.query.runtime.matchers.tuple.Tuples;
4import tools.refinery.store.model.Interpretation;
4import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl; 5import tools.refinery.store.query.viatra.internal.ViatraModelQueryAdapterImpl;
5import tools.refinery.store.query.view.TuplePreservingRelationView; 6import tools.refinery.store.query.view.TuplePreservingRelationView;
6import tools.refinery.store.tuple.Tuple; 7import tools.refinery.store.tuple.Tuple;
@@ -9,8 +10,8 @@ public class TuplePreservingRelationViewUpdateListener<T> extends RelationViewUp
9 private final TuplePreservingRelationView<T> view; 10 private final TuplePreservingRelationView<T> view;
10 11
11 TuplePreservingRelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter, 12 TuplePreservingRelationViewUpdateListener(ViatraModelQueryAdapterImpl adapter,
12 TuplePreservingRelationView<T> view) { 13 TuplePreservingRelationView<T> view, Interpretation<T> interpretation) {
13 super(adapter); 14 super(adapter, interpretation);
14 this.view = view; 15 this.view = view;
15 } 16 }
16 17
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java
new file mode 100644
index 00000000..90229b73
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/DiagonalQueryTest.java
@@ -0,0 +1,471 @@
1package tools.refinery.store.query.viatra;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import tools.refinery.store.model.ModelStore;
5import tools.refinery.store.query.ModelQuery;
6import tools.refinery.store.query.dnf.Dnf;
7import tools.refinery.store.query.dnf.Query;
8import tools.refinery.store.query.term.Variable;
9import tools.refinery.store.query.viatra.tests.QueryEngineTest;
10import tools.refinery.store.query.view.FunctionalRelationView;
11import tools.refinery.store.query.view.KeyOnlyRelationView;
12import tools.refinery.store.representation.Symbol;
13import tools.refinery.store.tuple.Tuple;
14
15import java.util.Map;
16import java.util.Optional;
17
18import static tools.refinery.store.query.literal.Literals.not;
19import static tools.refinery.store.query.term.int_.IntTerms.INT_SUM;
20import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertNullableResults;
21import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults;
22
23class DiagonalQueryTest {
24 @QueryEngineTest
25 void inputKeyNegationTest(QueryEvaluationHint hint) {
26 var person = new Symbol<>("Person", 1, Boolean.class, false);
27 var symbol = new Symbol<>("symbol", 4, Boolean.class, false);
28 var personView = new KeyOnlyRelationView<>(person);
29 var symbolView = new KeyOnlyRelationView<>(symbol);
30
31 var p1 = Variable.of("p1");
32 var p2 = Variable.of("p2");
33 var query = Query.builder("Diagonal")
34 .parameter(p1)
35 .clause(
36 personView.call(p1),
37 not(symbolView.call(p1, p1, p2, p2))
38 )
39 .build();
40
41 var store = ModelStore.builder()
42 .symbols(person, symbol)
43 .with(ViatraModelQuery.ADAPTER)
44 .defaultHint(hint)
45 .queries(query)
46 .build();
47
48 var model = store.createEmptyModel();
49 var personInterpretation = model.getInterpretation(person);
50 var symbolInterpretation = model.getInterpretation(symbol);
51 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
52 var queryResultSet = queryEngine.getResultSet(query);
53
54 personInterpretation.put(Tuple.of(0), true);
55 personInterpretation.put(Tuple.of(1), true);
56 personInterpretation.put(Tuple.of(2), true);
57
58 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
59 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
60 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
61 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
62
63 queryEngine.flushChanges();
64 assertResults(Map.of(
65 Tuple.of(0), false,
66 Tuple.of(1), true,
67 Tuple.of(2), true,
68 Tuple.of(3), false
69 ), queryResultSet);
70 }
71
72 @QueryEngineTest
73 void subQueryNegationTest(QueryEvaluationHint hint) {
74 var person = new Symbol<>("Person", 1, Boolean.class, false);
75 var symbol = new Symbol<>("symbol", 4, Boolean.class, false);
76 var personView = new KeyOnlyRelationView<>(person);
77 var symbolView = new KeyOnlyRelationView<>(symbol);
78
79 var p1 = Variable.of("p1");
80 var p2 = Variable.of("p2");
81 var p3 = Variable.of("p3");
82 var p4 = Variable.of("p4");
83 var subQuery = Dnf.builder("SubQuery")
84 .parameters(p1, p2, p3, p4)
85 .clause(
86 personView.call(p1),
87 symbolView.call(p1, p2, p3, p4)
88 )
89 .clause(
90 personView.call(p2),
91 symbolView.call(p1, p2, p3, p4)
92 )
93 .build();
94 var query = Query.builder("Diagonal")
95 .parameter(p1)
96 .clause(
97 personView.call(p1),
98 not(subQuery.call(p1, p1, p2, p2))
99 )
100 .build();
101
102 var store = ModelStore.builder()
103 .symbols(person, symbol)
104 .with(ViatraModelQuery.ADAPTER)
105 .defaultHint(hint)
106 .queries(query)
107 .build();
108
109 var model = store.createEmptyModel();
110 var personInterpretation = model.getInterpretation(person);
111 var symbolInterpretation = model.getInterpretation(symbol);
112 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
113 var queryResultSet = queryEngine.getResultSet(query);
114
115 personInterpretation.put(Tuple.of(0), true);
116 personInterpretation.put(Tuple.of(1), true);
117 personInterpretation.put(Tuple.of(2), true);
118
119 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
120 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
121 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
122 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
123
124 queryEngine.flushChanges();
125 assertResults(Map.of(
126 Tuple.of(0), false,
127 Tuple.of(1), true,
128 Tuple.of(2), true,
129 Tuple.of(3), false
130 ), queryResultSet);
131 }
132
133 @QueryEngineTest
134 void inputKeyCountTest(QueryEvaluationHint hint) {
135 var person = new Symbol<>("Person", 1, Boolean.class, false);
136 var symbol = new Symbol<>("symbol", 4, Boolean.class, false);
137 var personView = new KeyOnlyRelationView<>(person);
138 var symbolView = new KeyOnlyRelationView<>(symbol);
139
140 var p1 = Variable.of("p1");
141 var p2 = Variable.of("p2");
142 var x = Variable.of("x", Integer.class);
143 var query = Query.builder("Diagonal")
144 .parameter(p1)
145 .output(x)
146 .clause(
147 personView.call(p1),
148 x.assign(symbolView.count(p1, p1, p2, p2))
149 )
150 .build();
151
152 var store = ModelStore.builder()
153 .symbols(person, symbol)
154 .with(ViatraModelQuery.ADAPTER)
155 .defaultHint(hint)
156 .queries(query)
157 .build();
158
159 var model = store.createEmptyModel();
160 var personInterpretation = model.getInterpretation(person);
161 var symbolInterpretation = model.getInterpretation(symbol);
162 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
163 var queryResultSet = queryEngine.getResultSet(query);
164
165 personInterpretation.put(Tuple.of(0), true);
166 personInterpretation.put(Tuple.of(1), true);
167 personInterpretation.put(Tuple.of(2), true);
168
169 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
170 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), true);
171 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
172 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
173 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
174
175 queryEngine.flushChanges();
176 assertNullableResults(Map.of(
177 Tuple.of(0), Optional.of(2),
178 Tuple.of(1), Optional.of(0),
179 Tuple.of(2), Optional.of(0),
180 Tuple.of(3), Optional.empty()
181 ), queryResultSet);
182 }
183
184 @QueryEngineTest
185 void subQueryCountTest(QueryEvaluationHint hint) {
186 var person = new Symbol<>("Person", 1, Boolean.class, false);
187 var symbol = new Symbol<>("symbol", 4, Boolean.class, false);
188 var personView = new KeyOnlyRelationView<>(person);
189 var symbolView = new KeyOnlyRelationView<>(symbol);
190
191 var p1 = Variable.of("p1");
192 var p2 = Variable.of("p2");
193 var p3 = Variable.of("p3");
194 var p4 = Variable.of("p4");
195 var x = Variable.of("x", Integer.class);
196 var subQuery = Dnf.builder("SubQuery")
197 .parameters(p1, p2, p3, p4)
198 .clause(
199 personView.call(p1),
200 symbolView.call(p1, p2, p3, p4)
201 )
202 .clause(
203 personView.call(p2),
204 symbolView.call(p1, p2, p3, p4)
205 )
206 .build();
207 var query = Query.builder("Diagonal")
208 .parameter(p1)
209 .output(x)
210 .clause(
211 personView.call(p1),
212 x.assign(subQuery.count(p1, p1, p2, p2))
213 )
214 .build();
215
216 var store = ModelStore.builder()
217 .symbols(person, symbol)
218 .with(ViatraModelQuery.ADAPTER)
219 .defaultHint(hint)
220 .queries(query)
221 .build();
222
223 var model = store.createEmptyModel();
224 var personInterpretation = model.getInterpretation(person);
225 var symbolInterpretation = model.getInterpretation(symbol);
226 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
227 var queryResultSet = queryEngine.getResultSet(query);
228
229 personInterpretation.put(Tuple.of(0), true);
230 personInterpretation.put(Tuple.of(1), true);
231 personInterpretation.put(Tuple.of(2), true);
232
233 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), true);
234 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), true);
235 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), true);
236 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), true);
237 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), true);
238
239 queryEngine.flushChanges();
240 assertNullableResults(Map.of(
241 Tuple.of(0), Optional.of(2),
242 Tuple.of(1), Optional.of(0),
243 Tuple.of(2), Optional.of(0),
244 Tuple.of(3), Optional.empty()
245 ), queryResultSet);
246 }
247
248 @QueryEngineTest
249 void inputKeyAggregationTest(QueryEvaluationHint hint) {
250 var person = new Symbol<>("Person", 1, Boolean.class, false);
251 var symbol = new Symbol<>("symbol", 4, Integer.class, null);
252 var personView = new KeyOnlyRelationView<>(person);
253 var symbolView = new FunctionalRelationView<>(symbol);
254
255 var p1 = Variable.of("p1");
256 var p2 = Variable.of("p2");
257 var x = Variable.of("x", Integer.class);
258 var y = Variable.of("y", Integer.class);
259 var query = Query.builder("Diagonal")
260 .parameter(p1)
261 .output(x)
262 .clause(
263 personView.call(p1),
264 x.assign(symbolView.aggregate(y, INT_SUM, p1, p1, p2, p2, y))
265 )
266 .build();
267
268 var store = ModelStore.builder()
269 .symbols(person, symbol)
270 .with(ViatraModelQuery.ADAPTER)
271 .defaultHint(hint)
272 .queries(query)
273 .build();
274
275 var model = store.createEmptyModel();
276 var personInterpretation = model.getInterpretation(person);
277 var symbolInterpretation = model.getInterpretation(symbol);
278 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
279 var queryResultSet = queryEngine.getResultSet(query);
280
281 personInterpretation.put(Tuple.of(0), true);
282 personInterpretation.put(Tuple.of(1), true);
283 personInterpretation.put(Tuple.of(2), true);
284
285 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), 1);
286 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), 2);
287 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), 10);
288 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), 11);
289 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), 12);
290
291 queryEngine.flushChanges();
292 assertNullableResults(Map.of(
293 Tuple.of(0), Optional.of(3),
294 Tuple.of(1), Optional.of(0),
295 Tuple.of(2), Optional.of(0),
296 Tuple.of(3), Optional.empty()
297 ), queryResultSet);
298 }
299
300 @QueryEngineTest
301 void subQueryAggregationTest(QueryEvaluationHint hint) {
302 var person = new Symbol<>("Person", 1, Boolean.class, false);
303 var symbol = new Symbol<>("symbol", 4, Integer.class, null);
304 var personView = new KeyOnlyRelationView<>(person);
305 var symbolView = new FunctionalRelationView<>(symbol);
306
307 var p1 = Variable.of("p1");
308 var p2 = Variable.of("p2");
309 var p3 = Variable.of("p3");
310 var p4 = Variable.of("p4");
311 var x = Variable.of("x", Integer.class);
312 var y = Variable.of("y", Integer.class);
313 var z = Variable.of("z", Integer.class);
314 var subQuery = Dnf.builder("SubQuery")
315 .parameters(p1, p2, p3, p4, x, y)
316 .clause(
317 personView.call(p1),
318 symbolView.call(p1, p2, p3, p4, x),
319 y.assign(x)
320 )
321 .clause(
322 personView.call(p2),
323 symbolView.call(p1, p2, p3, p4, x),
324 y.assign(x)
325 )
326 .build();
327 var query = Query.builder("Diagonal")
328 .parameter(p1)
329 .output(x)
330 .clause(
331 personView.call(p1),
332 x.assign(subQuery.aggregate(z, INT_SUM, p1, p1, p2, p2, z, z))
333 )
334 .build();
335
336 var store = ModelStore.builder()
337 .symbols(person, symbol)
338 .with(ViatraModelQuery.ADAPTER)
339 .defaultHint(hint)
340 .queries(query)
341 .build();
342
343 var model = store.createEmptyModel();
344 var personInterpretation = model.getInterpretation(person);
345 var symbolInterpretation = model.getInterpretation(symbol);
346 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
347 var queryResultSet = queryEngine.getResultSet(query);
348
349 personInterpretation.put(Tuple.of(0), true);
350 personInterpretation.put(Tuple.of(1), true);
351 personInterpretation.put(Tuple.of(2), true);
352
353 symbolInterpretation.put(Tuple.of(0, 0, 1, 1), 1);
354 symbolInterpretation.put(Tuple.of(0, 0, 2, 2), 2);
355 symbolInterpretation.put(Tuple.of(0, 0, 1, 2), 10);
356 symbolInterpretation.put(Tuple.of(1, 1, 0, 1), 11);
357 symbolInterpretation.put(Tuple.of(1, 2, 1, 1), 12);
358
359 queryEngine.flushChanges();
360 assertNullableResults(Map.of(
361 Tuple.of(0), Optional.of(3),
362 Tuple.of(1), Optional.of(0),
363 Tuple.of(2), Optional.of(0),
364 Tuple.of(3), Optional.empty()
365 ), queryResultSet);
366 }
367
368 @QueryEngineTest
369 void inputKeyTransitiveTest(QueryEvaluationHint hint) {
370 var person = new Symbol<>("Person", 1, Boolean.class, false);
371 var symbol = new Symbol<>("symbol", 2, Boolean.class, false);
372 var personView = new KeyOnlyRelationView<>(person);
373 var symbolView = new KeyOnlyRelationView<>(symbol);
374
375 var p1 = Variable.of("p1");
376 var query = Query.builder("Diagonal")
377 .parameter(p1)
378 .clause(
379 personView.call(p1),
380 symbolView.callTransitive(p1, p1)
381 )
382 .build();
383
384 var store = ModelStore.builder()
385 .symbols(person, symbol)
386 .with(ViatraModelQuery.ADAPTER)
387 .defaultHint(hint)
388 .queries(query)
389 .build();
390
391 var model = store.createEmptyModel();
392 var personInterpretation = model.getInterpretation(person);
393 var symbolInterpretation = model.getInterpretation(symbol);
394 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
395 var queryResultSet = queryEngine.getResultSet(query);
396
397 personInterpretation.put(Tuple.of(0), true);
398 personInterpretation.put(Tuple.of(1), true);
399 personInterpretation.put(Tuple.of(2), true);
400
401 symbolInterpretation.put(Tuple.of(0, 0), true);
402 symbolInterpretation.put(Tuple.of(0, 1), true);
403 symbolInterpretation.put(Tuple.of(1, 2), true);
404
405 queryEngine.flushChanges();
406 assertResults(Map.of(
407 Tuple.of(0), true,
408 Tuple.of(1), false,
409 Tuple.of(2), false,
410 Tuple.of(3), false
411 ), queryResultSet);
412 }
413
414 @QueryEngineTest
415 void subQueryTransitiveTest(QueryEvaluationHint hint) {
416 var person = new Symbol<>("Person", 1, Boolean.class, false);
417 var symbol = new Symbol<>("symbol", 2, Boolean.class, false);
418 var personView = new KeyOnlyRelationView<>(person);
419 var symbolView = new KeyOnlyRelationView<>(symbol);
420
421 var p1 = Variable.of("p1");
422 var p2 = Variable.of("p2");
423 var subQuery = Dnf.builder("SubQuery")
424 .parameters(p1, p2)
425 .clause(
426 personView.call(p1),
427 symbolView.call(p1, p2)
428 )
429 .clause(
430 personView.call(p2),
431 symbolView.call(p1, p2)
432 )
433 .build();
434 var query = Query.builder("Diagonal")
435 .parameter(p1)
436 .clause(
437 personView.call(p1),
438 subQuery.callTransitive(p1, p1)
439 )
440 .build();
441
442 var store = ModelStore.builder()
443 .symbols(person, symbol)
444 .with(ViatraModelQuery.ADAPTER)
445 .defaultHint(hint)
446 .queries(query)
447 .build();
448
449 var model = store.createEmptyModel();
450 var personInterpretation = model.getInterpretation(person);
451 var symbolInterpretation = model.getInterpretation(symbol);
452 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
453 var queryResultSet = queryEngine.getResultSet(query);
454
455 personInterpretation.put(Tuple.of(0), true);
456 personInterpretation.put(Tuple.of(1), true);
457 personInterpretation.put(Tuple.of(2), true);
458
459 symbolInterpretation.put(Tuple.of(0, 0), true);
460 symbolInterpretation.put(Tuple.of(0, 1), true);
461 symbolInterpretation.put(Tuple.of(1, 2), true);
462
463 queryEngine.flushChanges();
464 assertResults(Map.of(
465 Tuple.of(0), true,
466 Tuple.of(1), false,
467 Tuple.of(2), false,
468 Tuple.of(3), false
469 ), queryResultSet);
470 }
471}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
new file mode 100644
index 00000000..fa2a008f
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/FunctionalQueryTest.java
@@ -0,0 +1,607 @@
1package tools.refinery.store.query.viatra;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import tools.refinery.store.map.Cursor;
5import tools.refinery.store.model.ModelStore;
6import tools.refinery.store.query.ModelQuery;
7import tools.refinery.store.query.dnf.Dnf;
8import tools.refinery.store.query.dnf.Query;
9import tools.refinery.store.query.term.Variable;
10import tools.refinery.store.query.viatra.tests.QueryEngineTest;
11import tools.refinery.store.query.view.FilteredRelationView;
12import tools.refinery.store.query.view.FunctionalRelationView;
13import tools.refinery.store.query.view.KeyOnlyRelationView;
14import tools.refinery.store.representation.Symbol;
15import tools.refinery.store.representation.TruthValue;
16import tools.refinery.store.tuple.Tuple;
17
18import java.util.Map;
19import java.util.Optional;
20
21import static org.hamcrest.MatcherAssert.assertThat;
22import static org.hamcrest.Matchers.is;
23import static org.hamcrest.Matchers.nullValue;
24import static org.junit.jupiter.api.Assertions.assertAll;
25import static org.junit.jupiter.api.Assertions.assertThrows;
26import static tools.refinery.store.query.literal.Literals.assume;
27import static tools.refinery.store.query.term.int_.IntTerms.*;
28import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertNullableResults;
29import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults;
30
31class FunctionalQueryTest {
32 @QueryEngineTest
33 void inputKeyTest(QueryEvaluationHint hint) {
34 var person = new Symbol<>("Person", 1, Boolean.class, false);
35 var age = new Symbol<>("age", 1, Integer.class, null);
36 var personView = new KeyOnlyRelationView<>(person);
37 var ageView = new FunctionalRelationView<>(age);
38
39 var p1 = Variable.of("p1");
40 var x = Variable.of("x", Integer.class);
41 var query = Query.builder("InputKey")
42 .parameter(p1)
43 .output(x)
44 .clause(
45 personView.call(p1),
46 ageView.call(p1, x)
47 )
48 .build();
49
50 var store = ModelStore.builder()
51 .symbols(person, age)
52 .with(ViatraModelQuery.ADAPTER)
53 .defaultHint(hint)
54 .queries(query)
55 .build();
56
57 var model = store.createEmptyModel();
58 var personInterpretation = model.getInterpretation(person);
59 var ageInterpretation = model.getInterpretation(age);
60 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
61 var queryResultSet = queryEngine.getResultSet(query);
62
63 personInterpretation.put(Tuple.of(0), true);
64 personInterpretation.put(Tuple.of(1), true);
65
66 ageInterpretation.put(Tuple.of(0), 12);
67 ageInterpretation.put(Tuple.of(1), 24);
68 ageInterpretation.put(Tuple.of(2), 36);
69
70 queryEngine.flushChanges();
71 assertNullableResults(Map.of(
72 Tuple.of(0), Optional.of(12),
73 Tuple.of(1), Optional.of(24),
74 Tuple.of(2), Optional.empty()
75 ), queryResultSet);
76 }
77
78 @QueryEngineTest
79 void predicateTest(QueryEvaluationHint hint) {
80 var person = new Symbol<>("Person", 1, Boolean.class, false);
81 var age = new Symbol<>("age", 1, Integer.class, null);
82 var personView = new KeyOnlyRelationView<>(person);
83 var ageView = new FunctionalRelationView<>(age);
84
85 var p1 = Variable.of("p1");
86 var x = Variable.of("x", Integer.class);
87 var subQuery = Dnf.builder("SubQuery")
88 .parameters(p1, x)
89 .clause(
90 personView.call(p1),
91 ageView.call(p1, x)
92 )
93 .build();
94 var query = Query.builder("Predicate")
95 .parameter(p1)
96 .output(x)
97 .clause(
98 personView.call(p1),
99 subQuery.call(p1, x)
100 )
101 .build();
102
103 var store = ModelStore.builder()
104 .symbols(person, age)
105 .with(ViatraModelQuery.ADAPTER)
106 .defaultHint(hint)
107 .queries(query)
108 .build();
109
110 var model = store.createEmptyModel();
111 var personInterpretation = model.getInterpretation(person);
112 var ageInterpretation = model.getInterpretation(age);
113 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
114 var queryResultSet = queryEngine.getResultSet(query);
115
116 personInterpretation.put(Tuple.of(0), true);
117 personInterpretation.put(Tuple.of(1), true);
118
119 ageInterpretation.put(Tuple.of(0), 12);
120 ageInterpretation.put(Tuple.of(1), 24);
121 ageInterpretation.put(Tuple.of(2), 36);
122
123 queryEngine.flushChanges();
124 assertNullableResults(Map.of(
125 Tuple.of(0), Optional.of(12),
126 Tuple.of(1), Optional.of(24),
127 Tuple.of(2), Optional.empty()
128 ), queryResultSet);
129 }
130
131 @QueryEngineTest
132 void computationTest(QueryEvaluationHint hint) {
133 var person = new Symbol<>("Person", 1, Boolean.class, false);
134 var age = new Symbol<>("age", 1, Integer.class, null);
135 var personView = new KeyOnlyRelationView<>(person);
136 var ageView = new FunctionalRelationView<>(age);
137
138 var p1 = Variable.of("p1");
139 var x = Variable.of("x", Integer.class);
140 var y = Variable.of("y", Integer.class);
141 var query = Query.builder("Computation")
142 .parameter(p1)
143 .output(y)
144 .clause(
145 personView.call(p1),
146 ageView.call(p1, x),
147 y.assign(mul(x, constant(7)))
148 )
149 .build();
150
151 var store = ModelStore.builder()
152 .symbols(person, age)
153 .with(ViatraModelQuery.ADAPTER)
154 .defaultHint(hint)
155 .queries(query)
156 .build();
157
158 var model = store.createEmptyModel();
159 var personInterpretation = model.getInterpretation(person);
160 var ageInterpretation = model.getInterpretation(age);
161 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
162 var queryResultSet = queryEngine.getResultSet(query);
163
164 personInterpretation.put(Tuple.of(0), true);
165 personInterpretation.put(Tuple.of(1), true);
166
167 ageInterpretation.put(Tuple.of(0), 12);
168 ageInterpretation.put(Tuple.of(1), 24);
169
170 queryEngine.flushChanges();
171 assertNullableResults(Map.of(
172 Tuple.of(0), Optional.of(84),
173 Tuple.of(1), Optional.of(168),
174 Tuple.of(2), Optional.empty()
175 ), queryResultSet);
176 }
177
178 @QueryEngineTest
179 void inputKeyCountTest(QueryEvaluationHint hint) {
180 var person = new Symbol<>("Person", 1, Boolean.class, false);
181 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
182 var personView = new KeyOnlyRelationView<>(person);
183 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
184
185 var p1 = Variable.of("p1");
186 var p2 = Variable.of("p2");
187 var x = Variable.of("x", Integer.class);
188 var query = Query.builder("Count")
189 .parameter(p1)
190 .output(x)
191 .clause(
192 personView.call(p1),
193 x.assign(friendMustView.count(p1, p2))
194 )
195 .build();
196
197 var store = ModelStore.builder()
198 .symbols(person, friend)
199 .with(ViatraModelQuery.ADAPTER)
200 .defaultHint(hint)
201 .queries(query)
202 .build();
203
204 var model = store.createEmptyModel();
205 var personInterpretation = model.getInterpretation(person);
206 var friendInterpretation = model.getInterpretation(friend);
207 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
208 var queryResultSet = queryEngine.getResultSet(query);
209
210 personInterpretation.put(Tuple.of(0), true);
211 personInterpretation.put(Tuple.of(1), true);
212 personInterpretation.put(Tuple.of(2), true);
213
214 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
215 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
216 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
217
218 queryEngine.flushChanges();
219 assertNullableResults(Map.of(
220 Tuple.of(0), Optional.of(1),
221 Tuple.of(1), Optional.of(2),
222 Tuple.of(2), Optional.of(0),
223 Tuple.of(3), Optional.empty()
224 ), queryResultSet);
225 }
226
227 @QueryEngineTest
228 void predicateCountTest(QueryEvaluationHint hint) {
229 var person = new Symbol<>("Person", 1, Boolean.class, false);
230 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
231 var personView = new KeyOnlyRelationView<>(person);
232 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
233
234 var p1 = Variable.of("p1");
235 var p2 = Variable.of("p2");
236 var x = Variable.of("x", Integer.class);
237 var subQuery = Dnf.builder("SubQuery")
238 .parameters(p1, p2)
239 .clause(
240 personView.call(p1),
241 personView.call(p2),
242 friendMustView.call(p1, p2)
243 )
244 .build();
245 var query = Query.builder("Count")
246 .parameter(p1)
247 .output(x)
248 .clause(
249 personView.call(p1),
250 x.assign(subQuery.count(p1, p2))
251 )
252 .build();
253
254 var store = ModelStore.builder()
255 .symbols(person, friend)
256 .with(ViatraModelQuery.ADAPTER)
257 .defaultHint(hint)
258 .queries(query)
259 .build();
260
261 var model = store.createEmptyModel();
262 var personInterpretation = model.getInterpretation(person);
263 var friendInterpretation = model.getInterpretation(friend);
264 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
265 var queryResultSet = queryEngine.getResultSet(query);
266
267 personInterpretation.put(Tuple.of(0), true);
268 personInterpretation.put(Tuple.of(1), true);
269 personInterpretation.put(Tuple.of(2), true);
270
271 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
272 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
273 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
274
275 queryEngine.flushChanges();
276 assertNullableResults(Map.of(
277 Tuple.of(0), Optional.of(1),
278 Tuple.of(1), Optional.of(2),
279 Tuple.of(2), Optional.of(0),
280 Tuple.of(3), Optional.empty()
281 ), queryResultSet);
282 }
283
284 @QueryEngineTest
285 void inputKeyAggregationTest(QueryEvaluationHint hint) {
286 var age = new Symbol<>("age", 1, Integer.class, null);
287 var ageView = new FunctionalRelationView<>(age);
288
289 var p1 = Variable.of("p1");
290 var x = Variable.of("x", Integer.class);
291 var y = Variable.of("y", Integer.class);
292 var query = Query.builder("Aggregate")
293 .output(x)
294 .clause(
295 x.assign(ageView.aggregate(y, INT_SUM, p1, y))
296 )
297 .build();
298
299 var store = ModelStore.builder()
300 .symbols(age)
301 .with(ViatraModelQuery.ADAPTER)
302 .defaultHint(hint)
303 .queries(query)
304 .build();
305
306 var model = store.createEmptyModel();
307 var ageInterpretation = model.getInterpretation(age);
308 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
309 var queryResultSet = queryEngine.getResultSet(query);
310
311 ageInterpretation.put(Tuple.of(0), 12);
312 ageInterpretation.put(Tuple.of(1), 24);
313
314 queryEngine.flushChanges();
315 assertResults(Map.of(Tuple.of(), 36), queryResultSet);
316 }
317
318 @QueryEngineTest
319 void predicateAggregationTest(QueryEvaluationHint hint) {
320 var person = new Symbol<>("Person", 1, Boolean.class, false);
321 var age = new Symbol<>("age", 1, Integer.class, null);
322 var personView = new KeyOnlyRelationView<>(person);
323 var ageView = new FunctionalRelationView<>(age);
324
325 var p1 = Variable.of("p1");
326 var x = Variable.of("x", Integer.class);
327 var y = Variable.of("y", Integer.class);
328 var subQuery = Dnf.builder("SubQuery")
329 .parameters(p1, x)
330 .clause(
331 personView.call(p1),
332 ageView.call(p1, x)
333 )
334 .build();
335 var query = Query.builder("Aggregate")
336 .output(x)
337 .clause(
338 x.assign(subQuery.aggregate(y, INT_SUM, p1, y))
339 )
340 .build();
341
342 var store = ModelStore.builder()
343 .symbols(person, age)
344 .with(ViatraModelQuery.ADAPTER)
345 .defaultHint(hint)
346 .queries(query)
347 .build();
348
349 var model = store.createEmptyModel();
350 var personInterpretation = model.getInterpretation(person);
351 var ageInterpretation = model.getInterpretation(age);
352 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
353 var queryResultSet = queryEngine.getResultSet(query);
354
355 personInterpretation.put(Tuple.of(0), true);
356 personInterpretation.put(Tuple.of(1), true);
357
358 ageInterpretation.put(Tuple.of(0), 12);
359 ageInterpretation.put(Tuple.of(1), 24);
360
361 queryEngine.flushChanges();
362 assertResults(Map.of(Tuple.of(), 36), queryResultSet);
363 }
364
365 @QueryEngineTest
366 void extremeValueTest(QueryEvaluationHint hint) {
367 var person = new Symbol<>("Person", 1, Boolean.class, false);
368 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
369 var personView = new KeyOnlyRelationView<>(person);
370 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
371
372 var p1 = Variable.of("p1");
373 var p2 = Variable.of("p2");
374 var x = Variable.of("x", Integer.class);
375 var y = Variable.of("y", Integer.class);
376 var subQuery = Dnf.builder("SubQuery")
377 .parameters(p1, x)
378 .clause(
379 personView.call(p1),
380 x.assign(friendMustView.count(p1, p2))
381 )
382 .build();
383 var minQuery = Query.builder("Min")
384 .output(x)
385 .clause(
386 x.assign(subQuery.aggregate(y, INT_MIN, p1, y))
387 )
388 .build();
389 var maxQuery = Query.builder("Max")
390 .output(x)
391 .clause(
392 x.assign(subQuery.aggregate(y, INT_MAX, p1, y))
393 )
394 .build();
395
396 var store = ModelStore.builder()
397 .symbols(person, friend)
398 .with(ViatraModelQuery.ADAPTER)
399 .defaultHint(hint)
400 .queries(minQuery, maxQuery)
401 .build();
402
403 var model = store.createEmptyModel();
404 var personInterpretation = model.getInterpretation(person);
405 var friendInterpretation = model.getInterpretation(friend);
406 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
407 var minResultSet = queryEngine.getResultSet(minQuery);
408 var maxResultSet = queryEngine.getResultSet(maxQuery);
409
410 assertResults(Map.of(Tuple.of(), Integer.MAX_VALUE), minResultSet);
411 assertResults(Map.of(Tuple.of(), Integer.MIN_VALUE), maxResultSet);
412
413 personInterpretation.put(Tuple.of(0), true);
414 personInterpretation.put(Tuple.of(1), true);
415 personInterpretation.put(Tuple.of(2), true);
416
417 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
418 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
419 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
420
421 queryEngine.flushChanges();
422 assertResults(Map.of(Tuple.of(), 0), minResultSet);
423 assertResults(Map.of(Tuple.of(), 2), maxResultSet);
424
425 friendInterpretation.put(Tuple.of(2, 0), TruthValue.TRUE);
426 friendInterpretation.put(Tuple.of(2, 1), TruthValue.TRUE);
427
428 queryEngine.flushChanges();
429 assertResults(Map.of(Tuple.of(), 1), minResultSet);
430 assertResults(Map.of(Tuple.of(), 2), maxResultSet);
431
432 friendInterpretation.put(Tuple.of(0, 1), TruthValue.FALSE);
433 friendInterpretation.put(Tuple.of(1, 0), TruthValue.FALSE);
434 friendInterpretation.put(Tuple.of(2, 0), TruthValue.FALSE);
435
436 queryEngine.flushChanges();
437 assertResults(Map.of(Tuple.of(), 0), minResultSet);
438 assertResults(Map.of(Tuple.of(), 1), maxResultSet);
439 }
440
441 @QueryEngineTest
442 void invalidComputationTest(QueryEvaluationHint hint) {
443 var person = new Symbol<>("Person", 1, Boolean.class, false);
444 var age = new Symbol<>("age", 1, Integer.class, null);
445 var personView = new KeyOnlyRelationView<>(person);
446 var ageView = new FunctionalRelationView<>(age);
447
448 var p1 = Variable.of("p1");
449 var x = Variable.of("x", Integer.class);
450 var y = Variable.of("y", Integer.class);
451 var query = Query.builder("InvalidComputation")
452 .parameter(p1)
453 .output(y)
454 .clause(
455 personView.call(p1),
456 ageView.call(p1, x),
457 y.assign(div(constant(120), x))
458 )
459 .build();
460
461 var store = ModelStore.builder()
462 .symbols(person, age)
463 .with(ViatraModelQuery.ADAPTER)
464 .defaultHint(hint)
465 .queries(query)
466 .build();
467
468 var model = store.createEmptyModel();
469 var personInterpretation = model.getInterpretation(person);
470 var ageInterpretation = model.getInterpretation(age);
471 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
472 var queryResultSet = queryEngine.getResultSet(query);
473
474 personInterpretation.put(Tuple.of(0), true);
475 personInterpretation.put(Tuple.of(1), true);
476
477 ageInterpretation.put(Tuple.of(0), 0);
478 ageInterpretation.put(Tuple.of(1), 30);
479
480 queryEngine.flushChanges();
481 assertNullableResults(Map.of(
482 Tuple.of(0), Optional.empty(),
483 Tuple.of(1), Optional.of(4),
484 Tuple.of(2), Optional.empty()
485 ), queryResultSet);
486 }
487
488 @QueryEngineTest
489 void invalidAssumeTest(QueryEvaluationHint hint) {
490 var person = new Symbol<>("Person", 1, Boolean.class, false);
491 var age = new Symbol<>("age", 1, Integer.class, null);
492 var personView = new KeyOnlyRelationView<>(person);
493 var ageView = new FunctionalRelationView<>(age);
494
495 var p1 = Variable.of("p1");
496 var x = Variable.of("x", Integer.class);
497 var query = Query.builder("InvalidComputation")
498 .parameter(p1)
499 .clause(
500 personView.call(p1),
501 ageView.call(p1, x),
502 assume(lessEq(div(constant(120), x), constant(5)))
503 )
504 .build();
505
506 var store = ModelStore.builder()
507 .symbols(person, age)
508 .with(ViatraModelQuery.ADAPTER)
509 .defaultHint(hint)
510 .queries(query)
511 .build();
512
513 var model = store.createEmptyModel();
514 var personInterpretation = model.getInterpretation(person);
515 var ageInterpretation = model.getInterpretation(age);
516 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
517 var queryResultSet = queryEngine.getResultSet(query);
518
519 personInterpretation.put(Tuple.of(0), true);
520 personInterpretation.put(Tuple.of(1), true);
521 personInterpretation.put(Tuple.of(2), true);
522
523 ageInterpretation.put(Tuple.of(0), 0);
524 ageInterpretation.put(Tuple.of(1), 30);
525 ageInterpretation.put(Tuple.of(2), 20);
526
527 queryEngine.flushChanges();
528 assertResults(Map.of(
529 Tuple.of(0), false,
530 Tuple.of(1), true,
531 Tuple.of(2), false,
532 Tuple.of(3), false
533 ), queryResultSet);
534 }
535
536 @QueryEngineTest
537 void notFunctionalTest(QueryEvaluationHint hint) {
538 var person = new Symbol<>("Person", 1, Boolean.class, false);
539 var age = new Symbol<>("age", 1, Integer.class, null);
540 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
541 var personView = new KeyOnlyRelationView<>(person);
542 var ageView = new FunctionalRelationView<>(age);
543 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
544
545 var p1 = Variable.of("p1");
546 var p2 = Variable.of("p2");
547 var x = Variable.of("x", Integer.class);
548 var query = Query.builder("NotFunctional")
549 .parameter(p1)
550 .output(x)
551 .clause(
552 personView.call(p1),
553 friendMustView.call(p1, p2),
554 ageView.call(p2, x)
555 )
556 .build();
557
558 var store = ModelStore.builder()
559 .symbols(person, age, friend)
560 .with(ViatraModelQuery.ADAPTER)
561 .defaultHint(hint)
562 .query(query)
563 .build();
564
565 var model = store.createEmptyModel();
566 var personInterpretation = model.getInterpretation(person);
567 var ageInterpretation = model.getInterpretation(age);
568 var friendInterpretation = model.getInterpretation(friend);
569 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
570 var queryResultSet = queryEngine.getResultSet(query);
571
572 personInterpretation.put(Tuple.of(0), true);
573 personInterpretation.put(Tuple.of(1), true);
574 personInterpretation.put(Tuple.of(2), true);
575
576 ageInterpretation.put(Tuple.of(0), 24);
577 ageInterpretation.put(Tuple.of(1), 30);
578 ageInterpretation.put(Tuple.of(2), 36);
579
580 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
581 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
582 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
583
584 queryEngine.flushChanges();
585 var invalidTuple = Tuple.of(1);
586 var cursor = queryResultSet.getAll();
587 assertAll(
588 () -> assertThat("value for key 0", queryResultSet.get(Tuple.of(0)), is(30)),
589 () -> assertThrows(IllegalStateException.class, () -> queryResultSet.get(invalidTuple),
590 "multiple values for key 1"),
591 () -> assertThat("value for key 2", queryResultSet.get(Tuple.of(2)), is(nullValue())),
592 () -> assertThat("value for key 3", queryResultSet.get(Tuple.of(3)), is(nullValue()))
593 );
594 if (hint.getQueryBackendRequirementType() != QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH) {
595 // Local search doesn't support throwing an error on multiple function return values.
596 assertThat("results size", queryResultSet.size(), is(2));
597 assertThrows(IllegalStateException.class, () -> enumerateValues(cursor), "move cursor");
598 }
599 }
600
601 private static void enumerateValues(Cursor<?, ?> cursor) {
602 //noinspection StatementWithEmptyBody
603 while (cursor.move()) {
604 // Nothing do, just let the cursor move through the result set.
605 }
606 }
607}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
index 8b25419d..8a3f9d88 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTest.java
@@ -1,33 +1,38 @@
1package tools.refinery.store.query.viatra; 1package tools.refinery.store.query.viatra;
2 2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
3import org.junit.jupiter.api.Test; 4import org.junit.jupiter.api.Test;
4import tools.refinery.store.map.Cursor;
5import tools.refinery.store.model.ModelStore; 5import tools.refinery.store.model.ModelStore;
6import tools.refinery.store.query.Dnf;
7import tools.refinery.store.query.ModelQuery; 6import tools.refinery.store.query.ModelQuery;
8import tools.refinery.store.query.Variable; 7import tools.refinery.store.query.dnf.Dnf;
8import tools.refinery.store.query.dnf.Query;
9import tools.refinery.store.query.term.Variable;
10import tools.refinery.store.query.viatra.tests.QueryEngineTest;
9import tools.refinery.store.query.view.FilteredRelationView; 11import tools.refinery.store.query.view.FilteredRelationView;
12import tools.refinery.store.query.view.FunctionalRelationView;
10import tools.refinery.store.query.view.KeyOnlyRelationView; 13import tools.refinery.store.query.view.KeyOnlyRelationView;
11import tools.refinery.store.representation.Symbol; 14import tools.refinery.store.representation.Symbol;
12import tools.refinery.store.representation.TruthValue; 15import tools.refinery.store.representation.TruthValue;
13import tools.refinery.store.tuple.Tuple; 16import tools.refinery.store.tuple.Tuple;
14import tools.refinery.store.tuple.TupleLike;
15 17
16import java.util.HashSet; 18import java.util.Map;
17import java.util.Set;
18 19
19import static org.junit.jupiter.api.Assertions.*; 20import static org.junit.jupiter.api.Assertions.assertThrows;
21import static tools.refinery.store.query.literal.Literals.assume;
20import static tools.refinery.store.query.literal.Literals.not; 22import static tools.refinery.store.query.literal.Literals.not;
23import static tools.refinery.store.query.term.int_.IntTerms.constant;
24import static tools.refinery.store.query.term.int_.IntTerms.greaterEq;
25import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults;
21 26
22class QueryTest { 27class QueryTest {
23 @Test 28 @QueryEngineTest
24 void typeConstraintTest() { 29 void typeConstraintTest(QueryEvaluationHint hint) {
25 var person = new Symbol<>("Person", 1, Boolean.class, false); 30 var person = new Symbol<>("Person", 1, Boolean.class, false);
26 var asset = new Symbol<>("Asset", 1, Boolean.class, false); 31 var asset = new Symbol<>("Asset", 1, Boolean.class, false);
27 var personView = new KeyOnlyRelationView<>(person); 32 var personView = new KeyOnlyRelationView<>(person);
28 33
29 var p1 = new Variable("p1"); 34 var p1 = Variable.of("p1");
30 var predicate = Dnf.builder("TypeConstraint") 35 var predicate = Query.builder("TypeConstraint")
31 .parameters(p1) 36 .parameters(p1)
32 .clause(personView.call(p1)) 37 .clause(personView.call(p1))
33 .build(); 38 .build();
@@ -35,7 +40,8 @@ class QueryTest {
35 var store = ModelStore.builder() 40 var store = ModelStore.builder()
36 .symbols(person, asset) 41 .symbols(person, asset)
37 .with(ViatraModelQuery.ADAPTER) 42 .with(ViatraModelQuery.ADAPTER)
38 .queries(predicate) 43 .defaultHint(hint)
44 .query(predicate)
39 .build(); 45 .build();
40 46
41 var model = store.createEmptyModel(); 47 var model = store.createEmptyModel();
@@ -51,20 +57,23 @@ class QueryTest {
51 assetInterpretation.put(Tuple.of(2), true); 57 assetInterpretation.put(Tuple.of(2), true);
52 58
53 queryEngine.flushChanges(); 59 queryEngine.flushChanges();
54 assertEquals(2, predicateResultSet.countResults()); 60 assertResults(Map.of(
55 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0), Tuple.of(1))); 61 Tuple.of(0), true,
62 Tuple.of(1), true,
63 Tuple.of(2), false
64 ), predicateResultSet);
56 } 65 }
57 66
58 @Test 67 @QueryEngineTest
59 void relationConstraintTest() { 68 void relationConstraintTest(QueryEvaluationHint hint) {
60 var person = new Symbol<>("Person", 1, Boolean.class, false); 69 var person = new Symbol<>("Person", 1, Boolean.class, false);
61 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 70 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
62 var personView = new KeyOnlyRelationView<>(person); 71 var personView = new KeyOnlyRelationView<>(person);
63 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 72 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
64 73
65 var p1 = new Variable("p1"); 74 var p1 = Variable.of("p1");
66 var p2 = new Variable("p2"); 75 var p2 = Variable.of("p2");
67 var predicate = Dnf.builder("RelationConstraint") 76 var predicate = Query.builder("RelationConstraint")
68 .parameters(p1, p2) 77 .parameters(p1, p2)
69 .clause( 78 .clause(
70 personView.call(p1), 79 personView.call(p1),
@@ -76,6 +85,7 @@ class QueryTest {
76 var store = ModelStore.builder() 85 var store = ModelStore.builder()
77 .symbols(person, friend) 86 .symbols(person, friend)
78 .with(ViatraModelQuery.ADAPTER) 87 .with(ViatraModelQuery.ADAPTER)
88 .defaultHint(hint)
79 .queries(predicate) 89 .queries(predicate)
80 .build(); 90 .build();
81 91
@@ -85,8 +95,6 @@ class QueryTest {
85 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 95 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
86 var predicateResultSet = queryEngine.getResultSet(predicate); 96 var predicateResultSet = queryEngine.getResultSet(predicate);
87 97
88 assertEquals(0, predicateResultSet.countResults());
89
90 personInterpretation.put(Tuple.of(0), true); 98 personInterpretation.put(Tuple.of(0), true);
91 personInterpretation.put(Tuple.of(1), true); 99 personInterpretation.put(Tuple.of(1), true);
92 personInterpretation.put(Tuple.of(2), true); 100 personInterpretation.put(Tuple.of(2), true);
@@ -94,83 +102,27 @@ class QueryTest {
94 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE); 102 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
95 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE); 103 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
96 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 104 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
97 105 friendInterpretation.put(Tuple.of(1, 3), TruthValue.TRUE);
98 assertEquals(0, predicateResultSet.countResults());
99 assertFalse(predicateResultSet.hasResult(Tuple.of(0, 1)));
100 assertFalse(predicateResultSet.hasResult(Tuple.of(0, 2)));
101 106
102 queryEngine.flushChanges(); 107 queryEngine.flushChanges();
103 assertEquals(3, predicateResultSet.countResults()); 108 assertResults(Map.of(
104 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0), Tuple.of(1, 2))); 109 Tuple.of(0, 1), true,
105 assertTrue(predicateResultSet.hasResult(Tuple.of(0, 1))); 110 Tuple.of(1, 0), true,
106 assertFalse(predicateResultSet.hasResult(Tuple.of(0, 2))); 111 Tuple.of(1, 2), true,
112 Tuple.of(2, 1), false
113 ), predicateResultSet);
107 } 114 }
108 115
109 @Test 116 @QueryEngineTest
110 void andTest() { 117 void existTest(QueryEvaluationHint hint) {
111 var person = new Symbol<>("Person", 1, Boolean.class, false); 118 var person = new Symbol<>("Person", 1, Boolean.class, false);
112 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 119 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
113 var personView = new KeyOnlyRelationView<>(person); 120 var personView = new KeyOnlyRelationView<>(person);
114 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 121 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
115 122
116 var p1 = new Variable("p1"); 123 var p1 = Variable.of("p1");
117 var p2 = new Variable("p2"); 124 var p2 = Variable.of("p2");
118 var predicate = Dnf.builder("RelationConstraint") 125 var predicate = Query.builder("RelationConstraint")
119 .parameters(p1, p2)
120 .clause(
121 personView.call(p1),
122 personView.call(p2),
123 friendMustView.call(p1, p2),
124 friendMustView.call(p2, p1)
125 )
126 .build();
127
128 var store = ModelStore.builder()
129 .symbols(person, friend)
130 .with(ViatraModelQuery.ADAPTER)
131 .queries(predicate)
132 .build();
133
134 var model = store.createEmptyModel();
135 var personInterpretation = model.getInterpretation(person);
136 var friendInterpretation = model.getInterpretation(friend);
137 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
138 var predicateResultSet = queryEngine.getResultSet(predicate);
139
140 assertEquals(0, predicateResultSet.countResults());
141
142 personInterpretation.put(Tuple.of(0), true);
143 personInterpretation.put(Tuple.of(1), true);
144 personInterpretation.put(Tuple.of(2), true);
145
146 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
147 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
148
149 queryEngine.flushChanges();
150 assertEquals(0, predicateResultSet.countResults());
151
152 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
153 queryEngine.flushChanges();
154 assertEquals(2, predicateResultSet.countResults());
155 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0)));
156
157 friendInterpretation.put(Tuple.of(2, 0), TruthValue.TRUE);
158 queryEngine.flushChanges();
159 assertEquals(4, predicateResultSet.countResults());
160 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(1, 0), Tuple.of(0, 2),
161 Tuple.of(2, 0)));
162 }
163
164 @Test
165 void existTest() {
166 var person = new Symbol<>("Person", 1, Boolean.class, false);
167 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
168 var personView = new KeyOnlyRelationView<>(person);
169 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
170
171 var p1 = new Variable("p1");
172 var p2 = new Variable("p2");
173 var predicate = Dnf.builder("RelationConstraint")
174 .parameters(p1) 126 .parameters(p1)
175 .clause( 127 .clause(
176 personView.call(p1), 128 personView.call(p1),
@@ -182,6 +134,7 @@ class QueryTest {
182 var store = ModelStore.builder() 134 var store = ModelStore.builder()
183 .symbols(person, friend) 135 .symbols(person, friend)
184 .with(ViatraModelQuery.ADAPTER) 136 .with(ViatraModelQuery.ADAPTER)
137 .defaultHint(hint)
185 .queries(predicate) 138 .queries(predicate)
186 .build(); 139 .build();
187 140
@@ -198,16 +151,19 @@ class QueryTest {
198 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE); 151 friendInterpretation.put(Tuple.of(0, 1), TruthValue.TRUE);
199 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE); 152 friendInterpretation.put(Tuple.of(1, 0), TruthValue.TRUE);
200 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 153 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
201 154 friendInterpretation.put(Tuple.of(3, 2), TruthValue.TRUE);
202 assertEquals(0, predicateResultSet.countResults());
203 155
204 queryEngine.flushChanges(); 156 queryEngine.flushChanges();
205 assertEquals(2, predicateResultSet.countResults()); 157 assertResults(Map.of(
206 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0), Tuple.of(1))); 158 Tuple.of(0), true,
159 Tuple.of(1), true,
160 Tuple.of(2), false,
161 Tuple.of(3), false
162 ), predicateResultSet);
207 } 163 }
208 164
209 @Test 165 @QueryEngineTest
210 void orTest() { 166 void orTest(QueryEvaluationHint hint) {
211 var person = new Symbol<>("Person", 1, Boolean.class, false); 167 var person = new Symbol<>("Person", 1, Boolean.class, false);
212 var animal = new Symbol<>("Animal", 1, Boolean.class, false); 168 var animal = new Symbol<>("Animal", 1, Boolean.class, false);
213 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 169 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
@@ -215,9 +171,9 @@ class QueryTest {
215 var animalView = new KeyOnlyRelationView<>(animal); 171 var animalView = new KeyOnlyRelationView<>(animal);
216 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 172 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
217 173
218 var p1 = new Variable("p1"); 174 var p1 = Variable.of("p1");
219 var p2 = new Variable("p2"); 175 var p2 = Variable.of("p2");
220 var predicate = Dnf.builder("Or") 176 var predicate = Query.builder("Or")
221 .parameters(p1, p2) 177 .parameters(p1, p2)
222 .clause( 178 .clause(
223 personView.call(p1), 179 personView.call(p1),
@@ -234,6 +190,7 @@ class QueryTest {
234 var store = ModelStore.builder() 190 var store = ModelStore.builder()
235 .symbols(person, animal, friend) 191 .symbols(person, animal, friend)
236 .with(ViatraModelQuery.ADAPTER) 192 .with(ViatraModelQuery.ADAPTER)
193 .defaultHint(hint)
237 .queries(predicate) 194 .queries(predicate)
238 .build(); 195 .build();
239 196
@@ -256,18 +213,23 @@ class QueryTest {
256 friendInterpretation.put(Tuple.of(3, 0), TruthValue.TRUE); 213 friendInterpretation.put(Tuple.of(3, 0), TruthValue.TRUE);
257 214
258 queryEngine.flushChanges(); 215 queryEngine.flushChanges();
259 assertEquals(2, predicateResultSet.countResults()); 216 assertResults(Map.of(
260 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1), Tuple.of(2, 3))); 217 Tuple.of(0, 1), true,
218 Tuple.of(0, 2), false,
219 Tuple.of(2, 3), true,
220 Tuple.of(3, 0), false,
221 Tuple.of(3, 2), false
222 ), predicateResultSet);
261 } 223 }
262 224
263 @Test 225 @QueryEngineTest
264 void equalityTest() { 226 void equalityTest(QueryEvaluationHint hint) {
265 var person = new Symbol<>("Person", 1, Boolean.class, false); 227 var person = new Symbol<>("Person", 1, Boolean.class, false);
266 var personView = new KeyOnlyRelationView<>(person); 228 var personView = new KeyOnlyRelationView<>(person);
267 229
268 var p1 = new Variable("p1"); 230 var p1 = Variable.of("p1");
269 var p2 = new Variable("p2"); 231 var p2 = Variable.of("p2");
270 var predicate = Dnf.builder("Equality") 232 var predicate = Query.builder("Equality")
271 .parameters(p1, p2) 233 .parameters(p1, p2)
272 .clause( 234 .clause(
273 personView.call(p1), 235 personView.call(p1),
@@ -279,6 +241,7 @@ class QueryTest {
279 var store = ModelStore.builder() 241 var store = ModelStore.builder()
280 .symbols(person) 242 .symbols(person)
281 .with(ViatraModelQuery.ADAPTER) 243 .with(ViatraModelQuery.ADAPTER)
244 .defaultHint(hint)
282 .queries(predicate) 245 .queries(predicate)
283 .build(); 246 .build();
284 247
@@ -292,21 +255,26 @@ class QueryTest {
292 personInterpretation.put(Tuple.of(2), true); 255 personInterpretation.put(Tuple.of(2), true);
293 256
294 queryEngine.flushChanges(); 257 queryEngine.flushChanges();
295 assertEquals(3, predicateResultSet.countResults()); 258 assertResults(Map.of(
296 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 0), Tuple.of(1, 1), Tuple.of(2, 2))); 259 Tuple.of(0, 0), true,
260 Tuple.of(1, 1), true,
261 Tuple.of(2, 2), true,
262 Tuple.of(0, 1), false,
263 Tuple.of(3, 3), false
264 ), predicateResultSet);
297 } 265 }
298 266
299 @Test 267 @QueryEngineTest
300 void inequalityTest() { 268 void inequalityTest(QueryEvaluationHint hint) {
301 var person = new Symbol<>("Person", 1, Boolean.class, false); 269 var person = new Symbol<>("Person", 1, Boolean.class, false);
302 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 270 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
303 var personView = new KeyOnlyRelationView<>(person); 271 var personView = new KeyOnlyRelationView<>(person);
304 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 272 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
305 273
306 var p1 = new Variable("p1"); 274 var p1 = Variable.of("p1");
307 var p2 = new Variable("p2"); 275 var p2 = Variable.of("p2");
308 var p3 = new Variable("p3"); 276 var p3 = Variable.of("p3");
309 var predicate = Dnf.builder("Inequality") 277 var predicate = Query.builder("Inequality")
310 .parameters(p1, p2, p3) 278 .parameters(p1, p2, p3)
311 .clause( 279 .clause(
312 personView.call(p1), 280 personView.call(p1),
@@ -320,6 +288,7 @@ class QueryTest {
320 var store = ModelStore.builder() 288 var store = ModelStore.builder()
321 .symbols(person, friend) 289 .symbols(person, friend)
322 .with(ViatraModelQuery.ADAPTER) 290 .with(ViatraModelQuery.ADAPTER)
291 .defaultHint(hint)
323 .queries(predicate) 292 .queries(predicate)
324 .build(); 293 .build();
325 294
@@ -337,19 +306,22 @@ class QueryTest {
337 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 306 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
338 307
339 queryEngine.flushChanges(); 308 queryEngine.flushChanges();
340 assertEquals(2, predicateResultSet.countResults()); 309 assertResults(Map.of(
341 compareMatchSets(predicateResultSet.allResults(), Set.of(Tuple.of(0, 1, 2), Tuple.of(1, 0, 2))); 310 Tuple.of(0, 1, 2), true,
311 Tuple.of(1, 0, 2), true,
312 Tuple.of(0, 0, 2), false
313 ), predicateResultSet);
342 } 314 }
343 315
344 @Test 316 @QueryEngineTest
345 void patternCallTest() { 317 void patternCallTest(QueryEvaluationHint hint) {
346 var person = new Symbol<>("Person", 1, Boolean.class, false); 318 var person = new Symbol<>("Person", 1, Boolean.class, false);
347 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 319 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
348 var personView = new KeyOnlyRelationView<>(person); 320 var personView = new KeyOnlyRelationView<>(person);
349 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 321 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
350 322
351 var p1 = new Variable("p1"); 323 var p1 = Variable.of("p1");
352 var p2 = new Variable("p2"); 324 var p2 = Variable.of("p2");
353 var friendPredicate = Dnf.builder("RelationConstraint") 325 var friendPredicate = Dnf.builder("RelationConstraint")
354 .parameters(p1, p2) 326 .parameters(p1, p2)
355 .clause( 327 .clause(
@@ -359,9 +331,9 @@ class QueryTest {
359 ) 331 )
360 .build(); 332 .build();
361 333
362 var p3 = new Variable("p3"); 334 var p3 = Variable.of("p3");
363 var p4 = new Variable("p4"); 335 var p4 = Variable.of("p4");
364 var predicate = Dnf.builder("PositivePatternCall") 336 var predicate = Query.builder("PositivePatternCall")
365 .parameters(p3, p4) 337 .parameters(p3, p4)
366 .clause( 338 .clause(
367 personView.call(p3), 339 personView.call(p3),
@@ -373,6 +345,7 @@ class QueryTest {
373 var store = ModelStore.builder() 345 var store = ModelStore.builder()
374 .symbols(person, friend) 346 .symbols(person, friend)
375 .with(ViatraModelQuery.ADAPTER) 347 .with(ViatraModelQuery.ADAPTER)
348 .defaultHint(hint)
376 .queries(predicate) 349 .queries(predicate)
377 .build(); 350 .build();
378 351
@@ -391,19 +364,24 @@ class QueryTest {
391 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 364 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
392 365
393 queryEngine.flushChanges(); 366 queryEngine.flushChanges();
394 assertEquals(3, predicateResultSet.countResults()); 367 assertResults(Map.of(
368 Tuple.of(0, 1), true,
369 Tuple.of(1, 0), true,
370 Tuple.of(1, 2), true,
371 Tuple.of(2, 1), false
372 ), predicateResultSet);
395 } 373 }
396 374
397 @Test 375 @QueryEngineTest
398 void negativeRelationViewTest() { 376 void negativeRelationViewTest(QueryEvaluationHint hint) {
399 var person = new Symbol<>("Person", 1, Boolean.class, false); 377 var person = new Symbol<>("Person", 1, Boolean.class, false);
400 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 378 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
401 var personView = new KeyOnlyRelationView<>(person); 379 var personView = new KeyOnlyRelationView<>(person);
402 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 380 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
403 381
404 var p1 = new Variable("p1"); 382 var p1 = Variable.of("p1");
405 var p2 = new Variable("p2"); 383 var p2 = Variable.of("p2");
406 var predicate = Dnf.builder("NegativePatternCall") 384 var predicate = Query.builder("NegativePatternCall")
407 .parameters(p1, p2) 385 .parameters(p1, p2)
408 .clause( 386 .clause(
409 personView.call(p1), 387 personView.call(p1),
@@ -415,6 +393,7 @@ class QueryTest {
415 var store = ModelStore.builder() 393 var store = ModelStore.builder()
416 .symbols(person, friend) 394 .symbols(person, friend)
417 .with(ViatraModelQuery.ADAPTER) 395 .with(ViatraModelQuery.ADAPTER)
396 .defaultHint(hint)
418 .queries(predicate) 397 .queries(predicate)
419 .build(); 398 .build();
420 399
@@ -433,18 +412,29 @@ class QueryTest {
433 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 412 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
434 413
435 queryEngine.flushChanges(); 414 queryEngine.flushChanges();
436 assertEquals(6, predicateResultSet.countResults()); 415 assertResults(Map.of(
416 Tuple.of(0, 0), true,
417 Tuple.of(0, 2), true,
418 Tuple.of(1, 1), true,
419 Tuple.of(2, 0), true,
420 Tuple.of(2, 1), true,
421 Tuple.of(2, 2), true,
422 Tuple.of(0, 1), false,
423 Tuple.of(1, 0), false,
424 Tuple.of(1, 2), false,
425 Tuple.of(0, 3), false
426 ), predicateResultSet);
437 } 427 }
438 428
439 @Test 429 @QueryEngineTest
440 void negativePatternCallTest() { 430 void negativePatternCallTest(QueryEvaluationHint hint) {
441 var person = new Symbol<>("Person", 1, Boolean.class, false); 431 var person = new Symbol<>("Person", 1, Boolean.class, false);
442 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 432 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
443 var personView = new KeyOnlyRelationView<>(person); 433 var personView = new KeyOnlyRelationView<>(person);
444 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 434 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
445 435
446 var p1 = new Variable("p1"); 436 var p1 = Variable.of("p1");
447 var p2 = new Variable("p2"); 437 var p2 = Variable.of("p2");
448 var friendPredicate = Dnf.builder("RelationConstraint") 438 var friendPredicate = Dnf.builder("RelationConstraint")
449 .parameters(p1, p2) 439 .parameters(p1, p2)
450 .clause( 440 .clause(
@@ -454,9 +444,9 @@ class QueryTest {
454 ) 444 )
455 .build(); 445 .build();
456 446
457 var p3 = new Variable("p3"); 447 var p3 = Variable.of("p3");
458 var p4 = new Variable("p4"); 448 var p4 = Variable.of("p4");
459 var predicate = Dnf.builder("NegativePatternCall") 449 var predicate = Query.builder("NegativePatternCall")
460 .parameters(p3, p4) 450 .parameters(p3, p4)
461 .clause( 451 .clause(
462 personView.call(p3), 452 personView.call(p3),
@@ -468,6 +458,7 @@ class QueryTest {
468 var store = ModelStore.builder() 458 var store = ModelStore.builder()
469 .symbols(person, friend) 459 .symbols(person, friend)
470 .with(ViatraModelQuery.ADAPTER) 460 .with(ViatraModelQuery.ADAPTER)
461 .defaultHint(hint)
471 .queries(predicate) 462 .queries(predicate)
472 .build(); 463 .build();
473 464
@@ -486,20 +477,31 @@ class QueryTest {
486 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 477 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
487 478
488 queryEngine.flushChanges(); 479 queryEngine.flushChanges();
489 assertEquals(6, predicateResultSet.countResults()); 480 assertResults(Map.of(
481 Tuple.of(0, 0), true,
482 Tuple.of(0, 2), true,
483 Tuple.of(1, 1), true,
484 Tuple.of(2, 0), true,
485 Tuple.of(2, 1), true,
486 Tuple.of(2, 2), true,
487 Tuple.of(0, 1), false,
488 Tuple.of(1, 0), false,
489 Tuple.of(1, 2), false,
490 Tuple.of(0, 3), false
491 ), predicateResultSet);
490 } 492 }
491 493
492 @Test 494 @QueryEngineTest
493 void negativeRelationViewWithQuantificationTest() { 495 void negativeRelationViewWithQuantificationTest(QueryEvaluationHint hint) {
494 var person = new Symbol<>("Person", 1, Boolean.class, false); 496 var person = new Symbol<>("Person", 1, Boolean.class, false);
495 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 497 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
496 var personView = new KeyOnlyRelationView<>(person); 498 var personView = new KeyOnlyRelationView<>(person);
497 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 499 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
498 500
499 var p1 = new Variable("p1"); 501 var p1 = Variable.of("p1");
500 var p2 = new Variable("p2"); 502 var p2 = Variable.of("p2");
501 503
502 var predicate = Dnf.builder("Count") 504 var predicate = Query.builder("Count")
503 .parameters(p1) 505 .parameters(p1)
504 .clause( 506 .clause(
505 personView.call(p1), 507 personView.call(p1),
@@ -510,6 +512,7 @@ class QueryTest {
510 var store = ModelStore.builder() 512 var store = ModelStore.builder()
511 .symbols(person, friend) 513 .symbols(person, friend)
512 .with(ViatraModelQuery.ADAPTER) 514 .with(ViatraModelQuery.ADAPTER)
515 .defaultHint(hint)
513 .queries(predicate) 516 .queries(predicate)
514 .build(); 517 .build();
515 518
@@ -527,18 +530,23 @@ class QueryTest {
527 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE); 530 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
528 531
529 queryEngine.flushChanges(); 532 queryEngine.flushChanges();
530 assertEquals(2, predicateResultSet.countResults()); 533 assertResults(Map.of(
534 Tuple.of(0), false,
535 Tuple.of(1), true,
536 Tuple.of(2), true,
537 Tuple.of(3), false
538 ), predicateResultSet);
531 } 539 }
532 540
533 @Test 541 @QueryEngineTest
534 void negativeWithQuantificationTest() { 542 void negativeWithQuantificationTest(QueryEvaluationHint hint) {
535 var person = new Symbol<>("Person", 1, Boolean.class, false); 543 var person = new Symbol<>("Person", 1, Boolean.class, false);
536 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 544 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
537 var personView = new KeyOnlyRelationView<>(person); 545 var personView = new KeyOnlyRelationView<>(person);
538 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 546 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
539 547
540 var p1 = new Variable("p1"); 548 var p1 = Variable.of("p1");
541 var p2 = new Variable("p2"); 549 var p2 = Variable.of("p2");
542 550
543 var called = Dnf.builder("Called") 551 var called = Dnf.builder("Called")
544 .parameters(p1, p2) 552 .parameters(p1, p2)
@@ -549,7 +557,7 @@ class QueryTest {
549 ) 557 )
550 .build(); 558 .build();
551 559
552 var predicate = Dnf.builder("Count") 560 var predicate = Query.builder("Count")
553 .parameters(p1) 561 .parameters(p1)
554 .clause( 562 .clause(
555 personView.call(p1), 563 personView.call(p1),
@@ -560,6 +568,7 @@ class QueryTest {
560 var store = ModelStore.builder() 568 var store = ModelStore.builder()
561 .symbols(person, friend) 569 .symbols(person, friend)
562 .with(ViatraModelQuery.ADAPTER) 570 .with(ViatraModelQuery.ADAPTER)
571 .defaultHint(hint)
563 .queries(predicate) 572 .queries(predicate)
564 .build(); 573 .build();
565 574
@@ -577,19 +586,24 @@ class QueryTest {
577 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE); 586 friendInterpretation.put(Tuple.of(0, 2), TruthValue.TRUE);
578 587
579 queryEngine.flushChanges(); 588 queryEngine.flushChanges();
580 assertEquals(2, predicateResultSet.countResults()); 589 assertResults(Map.of(
590 Tuple.of(0), false,
591 Tuple.of(1), true,
592 Tuple.of(2), true,
593 Tuple.of(3), false
594 ), predicateResultSet);
581 } 595 }
582 596
583 @Test 597 @QueryEngineTest
584 void transitiveRelationViewTest() { 598 void transitiveRelationViewTest(QueryEvaluationHint hint) {
585 var person = new Symbol<>("Person", 1, Boolean.class, false); 599 var person = new Symbol<>("Person", 1, Boolean.class, false);
586 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 600 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
587 var personView = new KeyOnlyRelationView<>(person); 601 var personView = new KeyOnlyRelationView<>(person);
588 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 602 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
589 603
590 var p1 = new Variable("p1"); 604 var p1 = Variable.of("p1");
591 var p2 = new Variable("p2"); 605 var p2 = Variable.of("p2");
592 var predicate = Dnf.builder("TransitivePatternCall") 606 var predicate = Query.builder("TransitivePatternCall")
593 .parameters(p1, p2) 607 .parameters(p1, p2)
594 .clause( 608 .clause(
595 personView.call(p1), 609 personView.call(p1),
@@ -601,6 +615,7 @@ class QueryTest {
601 var store = ModelStore.builder() 615 var store = ModelStore.builder()
602 .symbols(person, friend) 616 .symbols(person, friend)
603 .with(ViatraModelQuery.ADAPTER) 617 .with(ViatraModelQuery.ADAPTER)
618 .defaultHint(hint)
604 .queries(predicate) 619 .queries(predicate)
605 .build(); 620 .build();
606 621
@@ -618,18 +633,29 @@ class QueryTest {
618 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 633 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
619 634
620 queryEngine.flushChanges(); 635 queryEngine.flushChanges();
621 assertEquals(3, predicateResultSet.countResults()); 636 assertResults(Map.of(
637 Tuple.of(0, 0), false,
638 Tuple.of(0, 1), true,
639 Tuple.of(0, 2), true,
640 Tuple.of(1, 0), false,
641 Tuple.of(1, 1), false,
642 Tuple.of(1, 2), true,
643 Tuple.of(2, 0), false,
644 Tuple.of(2, 1), false,
645 Tuple.of(2, 2), false,
646 Tuple.of(2, 3), false
647 ), predicateResultSet);
622 } 648 }
623 649
624 @Test 650 @QueryEngineTest
625 void transitivePatternCallTest() { 651 void transitivePatternCallTest(QueryEvaluationHint hint) {
626 var person = new Symbol<>("Person", 1, Boolean.class, false); 652 var person = new Symbol<>("Person", 1, Boolean.class, false);
627 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE); 653 var friend = new Symbol<>("friend", 2, TruthValue.class, TruthValue.FALSE);
628 var personView = new KeyOnlyRelationView<>(person); 654 var personView = new KeyOnlyRelationView<>(person);
629 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must); 655 var friendMustView = new FilteredRelationView<>(friend, "must", TruthValue::must);
630 656
631 var p1 = new Variable("p1"); 657 var p1 = Variable.of("p1");
632 var p2 = new Variable("p2"); 658 var p2 = Variable.of("p2");
633 var friendPredicate = Dnf.builder("RelationConstraint") 659 var friendPredicate = Dnf.builder("RelationConstraint")
634 .parameters(p1, p2) 660 .parameters(p1, p2)
635 .clause( 661 .clause(
@@ -639,9 +665,9 @@ class QueryTest {
639 ) 665 )
640 .build(); 666 .build();
641 667
642 var p3 = new Variable("p3"); 668 var p3 = Variable.of("p3");
643 var p4 = new Variable("p4"); 669 var p4 = Variable.of("p4");
644 var predicate = Dnf.builder("TransitivePatternCall") 670 var predicate = Query.builder("TransitivePatternCall")
645 .parameters(p3, p4) 671 .parameters(p3, p4)
646 .clause( 672 .clause(
647 personView.call(p3), 673 personView.call(p3),
@@ -653,6 +679,7 @@ class QueryTest {
653 var store = ModelStore.builder() 679 var store = ModelStore.builder()
654 .symbols(person, friend) 680 .symbols(person, friend)
655 .with(ViatraModelQuery.ADAPTER) 681 .with(ViatraModelQuery.ADAPTER)
682 .defaultHint(hint)
656 .queries(predicate) 683 .queries(predicate)
657 .build(); 684 .build();
658 685
@@ -670,15 +697,71 @@ class QueryTest {
670 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE); 697 friendInterpretation.put(Tuple.of(1, 2), TruthValue.TRUE);
671 698
672 queryEngine.flushChanges(); 699 queryEngine.flushChanges();
673 assertEquals(3, predicateResultSet.countResults()); 700 assertResults(Map.of(
701 Tuple.of(0, 0), false,
702 Tuple.of(0, 1), true,
703 Tuple.of(0, 2), true,
704 Tuple.of(1, 0), false,
705 Tuple.of(1, 1), false,
706 Tuple.of(1, 2), true,
707 Tuple.of(2, 0), false,
708 Tuple.of(2, 1), false,
709 Tuple.of(2, 2), false,
710 Tuple.of(2, 3), false
711 ), predicateResultSet);
712 }
713
714 @QueryEngineTest
715 void assumeTest(QueryEvaluationHint hint) {
716 var person = new Symbol<>("Person", 1, Boolean.class, false);
717 var age = new Symbol<>("age", 1, Integer.class, null);
718 var personView = new KeyOnlyRelationView<>(person);
719 var ageView = new FunctionalRelationView<>(age);
720
721 var p1 = Variable.of("p1");
722 var x = Variable.of("x", Integer.class);
723 var query = Query.builder("Constraint")
724 .parameter(p1)
725 .clause(
726 personView.call(p1),
727 ageView.call(p1, x),
728 assume(greaterEq(x, constant(18)))
729 )
730 .build();
731
732 var store = ModelStore.builder()
733 .symbols(person, age)
734 .with(ViatraModelQuery.ADAPTER)
735 .defaultHint(hint)
736 .queries(query)
737 .build();
738
739 var model = store.createEmptyModel();
740 var personInterpretation = model.getInterpretation(person);
741 var ageInterpretation = model.getInterpretation(age);
742 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
743 var queryResultSet = queryEngine.getResultSet(query);
744
745 personInterpretation.put(Tuple.of(0), true);
746 personInterpretation.put(Tuple.of(1), true);
747
748 ageInterpretation.put(Tuple.of(0), 12);
749 ageInterpretation.put(Tuple.of(1), 24);
750
751 queryEngine.flushChanges();
752 assertResults(Map.of(
753 Tuple.of(0), false,
754 Tuple.of(1), true,
755 Tuple.of(2), false
756 ), queryResultSet);
674 } 757 }
675 758
676 @Test 759 @Test
677 void alwaysFalseTest() { 760 void alwaysFalseTest() {
678 var person = new Symbol<>("Person", 1, Boolean.class, false); 761 var person = new Symbol<>("Person", 1, Boolean.class, false);
679 762
680 var p1 = new Variable("p1"); 763 var p1 = Variable.of("p1");
681 var predicate = Dnf.builder("AlwaysFalse").parameters(p1).build(); 764 var predicate = Query.builder("AlwaysFalse").parameters(p1).build();
682 765
683 var store = ModelStore.builder() 766 var store = ModelStore.builder()
684 .symbols(person) 767 .symbols(person)
@@ -696,28 +779,19 @@ class QueryTest {
696 personInterpretation.put(Tuple.of(2), true); 779 personInterpretation.put(Tuple.of(2), true);
697 780
698 queryEngine.flushChanges(); 781 queryEngine.flushChanges();
699 assertEquals(0, predicateResultSet.countResults()); 782 assertResults(Map.of(), predicateResultSet);
700 } 783 }
701 784
702 @Test 785 @Test
703 void alwaysTrueTest() { 786 void alwaysTrueTest() {
704 var person = new Symbol<>("Person", 1, Boolean.class, false); 787 var person = new Symbol<>("Person", 1, Boolean.class, false);
705 788
706 var p1 = new Variable("p1"); 789 var p1 = Variable.of("p1");
707 var predicate = Dnf.builder("AlwaysTrue").parameters(p1).clause().build(); 790 var predicate = Query.builder("AlwaysTrue").parameters(p1).clause().build();
708 791
709 var storeBuilder = ModelStore.builder().symbols(person); 792 var storeBuilder = ModelStore.builder().symbols(person);
710 var queryBuilder = storeBuilder.with(ViatraModelQuery.ADAPTER); 793 var queryBuilder = storeBuilder.with(ViatraModelQuery.ADAPTER);
711 794
712 assertThrows(IllegalArgumentException.class, () -> queryBuilder.queries(predicate)); 795 assertThrows(IllegalArgumentException.class, () -> queryBuilder.queries(predicate));
713 } 796 }
714
715 private static void compareMatchSets(Cursor<TupleLike, Boolean> cursor, Set<Tuple> expected) {
716 Set<Tuple> translatedMatchSet = new HashSet<>();
717 while (cursor.move()) {
718 var element = cursor.getKey();
719 translatedMatchSet.add(element.toTuple());
720 }
721 assertEquals(expected, translatedMatchSet);
722 }
723} 797}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
index 461685b5..abd49341 100644
--- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/QueryTransactionTest.java
@@ -1,25 +1,160 @@
1package tools.refinery.store.query.viatra; 1package tools.refinery.store.query.viatra;
2 2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import org.junit.jupiter.api.Disabled;
3import org.junit.jupiter.api.Test; 5import org.junit.jupiter.api.Test;
4import tools.refinery.store.model.ModelStore; 6import tools.refinery.store.model.ModelStore;
5import tools.refinery.store.query.Dnf;
6import tools.refinery.store.query.ModelQuery; 7import tools.refinery.store.query.ModelQuery;
7import tools.refinery.store.query.Variable; 8import tools.refinery.store.query.dnf.Query;
9import tools.refinery.store.query.term.Variable;
10import tools.refinery.store.query.view.FilteredRelationView;
11import tools.refinery.store.query.view.FunctionalRelationView;
8import tools.refinery.store.query.view.KeyOnlyRelationView; 12import tools.refinery.store.query.view.KeyOnlyRelationView;
9import tools.refinery.store.representation.Symbol; 13import tools.refinery.store.representation.Symbol;
10import tools.refinery.store.tuple.Tuple; 14import tools.refinery.store.tuple.Tuple;
11 15
12import static org.junit.jupiter.api.Assertions.*; 16import java.util.Map;
17import java.util.Optional;
18
19import static org.junit.jupiter.api.Assertions.assertFalse;
20import static org.junit.jupiter.api.Assertions.assertTrue;
21import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertNullableResults;
22import static tools.refinery.store.query.viatra.tests.QueryAssertions.assertResults;
13 23
14class QueryTransactionTest { 24class QueryTransactionTest {
15 @Test 25 @Test
16 void flushTest() { 26 void flushTest() {
17 var person = new Symbol<>("Person", 1, Boolean.class, false); 27 var person = new Symbol<>("Person", 1, Boolean.class, false);
28 var personView = new KeyOnlyRelationView<>(person);
29
30 var p1 = Variable.of("p1");
31 var predicate = Query.builder("TypeConstraint")
32 .parameters(p1)
33 .clause(personView.call(p1))
34 .build();
35
36 var store = ModelStore.builder()
37 .symbols(person)
38 .with(ViatraModelQuery.ADAPTER)
39 .queries(predicate)
40 .build();
41
42 var model = store.createEmptyModel();
43 var personInterpretation = model.getInterpretation(person);
44 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
45 var predicateResultSet = queryEngine.getResultSet(predicate);
46
47 assertResults(Map.of(
48 Tuple.of(0), false,
49 Tuple.of(1), false,
50 Tuple.of(2), false,
51 Tuple.of(3), false
52 ), predicateResultSet);
53 assertFalse(queryEngine.hasPendingChanges());
54
55 personInterpretation.put(Tuple.of(0), true);
56 personInterpretation.put(Tuple.of(1), true);
57
58 assertResults(Map.of(
59 Tuple.of(0), false,
60 Tuple.of(1), false,
61 Tuple.of(2), false,
62 Tuple.of(3), false
63 ), predicateResultSet);
64 assertTrue(queryEngine.hasPendingChanges());
65
66 queryEngine.flushChanges();
67 assertResults(Map.of(
68 Tuple.of(0), true,
69 Tuple.of(1), true,
70 Tuple.of(2), false,
71 Tuple.of(3), false
72 ), predicateResultSet);
73 assertFalse(queryEngine.hasPendingChanges());
74
75 personInterpretation.put(Tuple.of(1), false);
76 personInterpretation.put(Tuple.of(2), true);
77
78 assertResults(Map.of(
79 Tuple.of(0), true,
80 Tuple.of(1), true,
81 Tuple.of(2), false,
82 Tuple.of(3), false
83 ), predicateResultSet);
84 assertTrue(queryEngine.hasPendingChanges());
85
86 queryEngine.flushChanges();
87 assertResults(Map.of(
88 Tuple.of(0), true,
89 Tuple.of(1), false,
90 Tuple.of(2), true,
91 Tuple.of(3), false
92 ), predicateResultSet);
93 assertFalse(queryEngine.hasPendingChanges());
94 }
95
96 @Test
97 void localSearchTest() {
98 var person = new Symbol<>("Person", 1, Boolean.class, false);
99 var personView = new KeyOnlyRelationView<>(person);
100
101 var p1 = Variable.of("p1");
102 var predicate = Query.builder("TypeConstraint")
103 .parameters(p1)
104 .clause(personView.call(p1))
105 .build();
106
107 var store = ModelStore.builder()
108 .symbols(person)
109 .with(ViatraModelQuery.ADAPTER)
110 .defaultHint(new QueryEvaluationHint(null, QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH))
111 .queries(predicate)
112 .build();
113
114 var model = store.createEmptyModel();
115 var personInterpretation = model.getInterpretation(person);
116 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
117 var predicateResultSet = queryEngine.getResultSet(predicate);
118
119 assertResults(Map.of(
120 Tuple.of(0), false,
121 Tuple.of(1), false,
122 Tuple.of(2), false,
123 Tuple.of(3), false
124 ), predicateResultSet);
125 assertFalse(queryEngine.hasPendingChanges());
126
127 personInterpretation.put(Tuple.of(0), true);
128 personInterpretation.put(Tuple.of(1), true);
129
130 assertResults(Map.of(
131 Tuple.of(0), true,
132 Tuple.of(1), true,
133 Tuple.of(2), false,
134 Tuple.of(3), false
135 ), predicateResultSet);
136 assertFalse(queryEngine.hasPendingChanges());
137
138 personInterpretation.put(Tuple.of(1), false);
139 personInterpretation.put(Tuple.of(2), true);
140
141 assertResults(Map.of(
142 Tuple.of(0), true,
143 Tuple.of(1), false,
144 Tuple.of(2), true,
145 Tuple.of(3), false
146 ), predicateResultSet);
147 assertFalse(queryEngine.hasPendingChanges());
148 }
149
150 @Test
151 void unrelatedChangesTest() {
152 var person = new Symbol<>("Person", 1, Boolean.class, false);
18 var asset = new Symbol<>("Asset", 1, Boolean.class, false); 153 var asset = new Symbol<>("Asset", 1, Boolean.class, false);
19 var personView = new KeyOnlyRelationView<>(person); 154 var personView = new KeyOnlyRelationView<>(person);
20 155
21 var p1 = new Variable("p1"); 156 var p1 = Variable.of("p1");
22 var predicate = Dnf.builder("TypeConstraint") 157 var predicate = Query.builder("TypeConstraint")
23 .parameters(p1) 158 .parameters(p1)
24 .clause(personView.call(p1)) 159 .clause(personView.call(p1))
25 .build(); 160 .build();
@@ -36,7 +171,6 @@ class QueryTransactionTest {
36 var queryEngine = model.getAdapter(ModelQuery.ADAPTER); 171 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
37 var predicateResultSet = queryEngine.getResultSet(predicate); 172 var predicateResultSet = queryEngine.getResultSet(predicate);
38 173
39 assertEquals(0, predicateResultSet.countResults());
40 assertFalse(queryEngine.hasPendingChanges()); 174 assertFalse(queryEngine.hasPendingChanges());
41 175
42 personInterpretation.put(Tuple.of(0), true); 176 personInterpretation.put(Tuple.of(0), true);
@@ -45,19 +179,245 @@ class QueryTransactionTest {
45 assetInterpretation.put(Tuple.of(1), true); 179 assetInterpretation.put(Tuple.of(1), true);
46 assetInterpretation.put(Tuple.of(2), true); 180 assetInterpretation.put(Tuple.of(2), true);
47 181
48 assertEquals(0, predicateResultSet.countResults()); 182 assertResults(Map.of(
183 Tuple.of(0), false,
184 Tuple.of(1), false,
185 Tuple.of(2), false,
186 Tuple.of(3), false,
187 Tuple.of(4), false
188 ), predicateResultSet);
49 assertTrue(queryEngine.hasPendingChanges()); 189 assertTrue(queryEngine.hasPendingChanges());
50 190
51 queryEngine.flushChanges(); 191 queryEngine.flushChanges();
52 assertEquals(2, predicateResultSet.countResults()); 192 assertResults(Map.of(
193 Tuple.of(0), true,
194 Tuple.of(1), true,
195 Tuple.of(2), false,
196 Tuple.of(3), false,
197 Tuple.of(4), false
198 ), predicateResultSet);
53 assertFalse(queryEngine.hasPendingChanges()); 199 assertFalse(queryEngine.hasPendingChanges());
54 200
55 personInterpretation.put(Tuple.of(4), true); 201 assetInterpretation.put(Tuple.of(3), true);
56 assertEquals(2, predicateResultSet.countResults()); 202 assertFalse(queryEngine.hasPendingChanges());
57 assertTrue(queryEngine.hasPendingChanges()); 203
204 assertResults(Map.of(
205 Tuple.of(0), true,
206 Tuple.of(1), true,
207 Tuple.of(2), false,
208 Tuple.of(3), false,
209 Tuple.of(4), false
210 ), predicateResultSet);
211
212 queryEngine.flushChanges();
213 assertResults(Map.of(
214 Tuple.of(0), true,
215 Tuple.of(1), true,
216 Tuple.of(2), false,
217 Tuple.of(3), false,
218 Tuple.of(4), false
219 ), predicateResultSet);
220 assertFalse(queryEngine.hasPendingChanges());
221 }
222
223 @Test
224 void tupleChangingChangeTest() {
225 var person = new Symbol<>("Person", 1, Boolean.class, false);
226 var age = new Symbol<>("age", 1, Integer.class, null);
227 var personView = new KeyOnlyRelationView<>(person);
228 var ageView = new FunctionalRelationView<>(age);
229
230 var p1 = Variable.of("p1");
231 var x = Variable.of("x", Integer.class);
232 var query = Query.builder()
233 .parameter(p1)
234 .output(x)
235 .clause(
236 personView.call(p1),
237 ageView.call(p1, x)
238 )
239 .build();
240
241 var store = ModelStore.builder()
242 .symbols(person, age)
243 .with(ViatraModelQuery.ADAPTER)
244 .query(query)
245 .build();
246
247 var model = store.createEmptyModel();
248 var personInterpretation = model.getInterpretation(person);
249 var ageInterpretation = model.getInterpretation(age);
250 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
251 var queryResultSet = queryEngine.getResultSet(query);
252
253 personInterpretation.put(Tuple.of(0), true);
254
255 ageInterpretation.put(Tuple.of(0), 24);
256
257 queryEngine.flushChanges();
258 assertResults(Map.of(Tuple.of(0), 24), queryResultSet);
259
260 ageInterpretation.put(Tuple.of(0), 25);
261
262 queryEngine.flushChanges();
263 assertResults(Map.of(Tuple.of(0), 25), queryResultSet);
264
265 ageInterpretation.put(Tuple.of(0), null);
266
267 queryEngine.flushChanges();
268 assertNullableResults(Map.of(Tuple.of(0), Optional.empty()), queryResultSet);
269 }
270
271 @Test
272 void tuplePreservingUnchangedTest() {
273 var person = new Symbol<>("Person", 1, Boolean.class, false);
274 var age = new Symbol<>("age", 1, Integer.class, null);
275 var personView = new KeyOnlyRelationView<>(person);
276 var adultView = new FilteredRelationView<>(age, "adult", n -> n != null && n >= 18);
277
278 var p1 = Variable.of("p1");
279 var x = Variable.of("x", Integer.class);
280 var query = Query.builder()
281 .parameter(p1)
282 .clause(
283 personView.call(p1),
284 adultView.call(p1)
285 )
286 .build();
287
288 var store = ModelStore.builder()
289 .symbols(person, age)
290 .with(ViatraModelQuery.ADAPTER)
291 .query(query)
292 .build();
293
294 var model = store.createEmptyModel();
295 var personInterpretation = model.getInterpretation(person);
296 var ageInterpretation = model.getInterpretation(age);
297 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
298 var queryResultSet = queryEngine.getResultSet(query);
299
300 personInterpretation.put(Tuple.of(0), true);
301
302 ageInterpretation.put(Tuple.of(0), 24);
303
304 queryEngine.flushChanges();
305 assertResults(Map.of(Tuple.of(0), true), queryResultSet);
306
307 ageInterpretation.put(Tuple.of(0), 25);
308
309 queryEngine.flushChanges();
310 assertResults(Map.of(Tuple.of(0), true), queryResultSet);
311
312 ageInterpretation.put(Tuple.of(0), 17);
58 313
59 queryEngine.flushChanges(); 314 queryEngine.flushChanges();
60 assertEquals(3, predicateResultSet.countResults()); 315 assertResults(Map.of(Tuple.of(0), false), queryResultSet);
316 }
317
318 @Disabled("TODO Fix DiffCursor")
319 @Test
320 void commitAfterFlushTest() {
321 var person = new Symbol<>("Person", 1, Boolean.class, false);
322 var personView = new KeyOnlyRelationView<>(person);
323
324 var p1 = Variable.of("p1");
325 var predicate = Query.builder("TypeConstraint")
326 .parameters(p1)
327 .clause(personView.call(p1))
328 .build();
329
330 var store = ModelStore.builder()
331 .symbols(person)
332 .with(ViatraModelQuery.ADAPTER)
333 .queries(predicate)
334 .build();
335
336 var model = store.createEmptyModel();
337 var personInterpretation = model.getInterpretation(person);
338 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
339 var predicateResultSet = queryEngine.getResultSet(predicate);
340
341 personInterpretation.put(Tuple.of(0), true);
342 personInterpretation.put(Tuple.of(1), true);
343
344 queryEngine.flushChanges();
345 assertResults(Map.of(
346 Tuple.of(0), true,
347 Tuple.of(1), true,
348 Tuple.of(2), false,
349 Tuple.of(3), false
350 ), predicateResultSet);
351
352 var state1 = model.commit();
353
354 personInterpretation.put(Tuple.of(1), false);
355 personInterpretation.put(Tuple.of(2), true);
356
357 queryEngine.flushChanges();
358 assertResults(Map.of(
359 Tuple.of(0), true,
360 Tuple.of(1), false,
361 Tuple.of(2), true,
362 Tuple.of(3), false
363 ), predicateResultSet);
364
365 model.restore(state1);
366
367 assertFalse(queryEngine.hasPendingChanges());
368 assertResults(Map.of(
369 Tuple.of(0), true,
370 Tuple.of(1), true,
371 Tuple.of(2), false,
372 Tuple.of(3), false
373 ), predicateResultSet);
374 }
375
376 @Disabled("TODO Fix DiffCursor")
377 @Test
378 void commitWithoutFlushTest() {
379 var person = new Symbol<>("Person", 1, Boolean.class, false);
380 var personView = new KeyOnlyRelationView<>(person);
381
382 var p1 = Variable.of("p1");
383 var predicate = Query.builder("TypeConstraint")
384 .parameters(p1)
385 .clause(personView.call(p1))
386 .build();
387
388 var store = ModelStore.builder()
389 .symbols(person)
390 .with(ViatraModelQuery.ADAPTER)
391 .queries(predicate)
392 .build();
393
394 var model = store.createEmptyModel();
395 var personInterpretation = model.getInterpretation(person);
396 var queryEngine = model.getAdapter(ModelQuery.ADAPTER);
397 var predicateResultSet = queryEngine.getResultSet(predicate);
398
399 personInterpretation.put(Tuple.of(0), true);
400 personInterpretation.put(Tuple.of(1), true);
401
402 assertResults(Map.of(), predicateResultSet);
403 assertTrue(queryEngine.hasPendingChanges());
404
405 var state1 = model.commit();
406
407 personInterpretation.put(Tuple.of(1), false);
408 personInterpretation.put(Tuple.of(2), true);
409
410 assertResults(Map.of(), predicateResultSet);
411 assertTrue(queryEngine.hasPendingChanges());
412
413 model.restore(state1);
414
415 assertResults(Map.of(
416 Tuple.of(0), true,
417 Tuple.of(1), true,
418 Tuple.of(2), false,
419 Tuple.of(3), false
420 ), predicateResultSet);
61 assertFalse(queryEngine.hasPendingChanges()); 421 assertFalse(queryEngine.hasPendingChanges());
62 } 422 }
63} 423}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java
new file mode 100644
index 00000000..6f50ec73
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryAssertions.java
@@ -0,0 +1,52 @@
1package tools.refinery.store.query.viatra.tests;
2
3import org.junit.jupiter.api.function.Executable;
4import tools.refinery.store.query.ResultSet;
5import tools.refinery.store.tuple.Tuple;
6
7import java.util.*;
8
9import static org.hamcrest.MatcherAssert.assertThat;
10import static org.hamcrest.Matchers.is;
11import static org.hamcrest.Matchers.nullValue;
12import static org.junit.jupiter.api.Assertions.assertAll;
13
14public final class QueryAssertions {
15 private QueryAssertions() {
16 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
17 }
18
19 public static <T> void assertNullableResults(Map<Tuple, Optional<T>> expected, ResultSet<T> resultSet) {
20 var nullableValuesMap = new LinkedHashMap<Tuple, T>(expected.size());
21 for (var entry : expected.entrySet()) {
22 nullableValuesMap.put(entry.getKey(), entry.getValue().orElse(null));
23 }
24 assertResults(nullableValuesMap, resultSet);
25 }
26
27 public static <T> void assertResults(Map<Tuple, T> expected, ResultSet<T> resultSet) {
28 var defaultValue = resultSet.getQuery().defaultValue();
29 var filteredExpected = new LinkedHashMap<Tuple, T>();
30 var executables = new ArrayList<Executable>();
31 for (var entry : expected.entrySet()) {
32 var key = entry.getKey();
33 var value = entry.getValue();
34 if (!Objects.equals(value, defaultValue)) {
35 filteredExpected.put(key, value);
36 }
37 executables.add(() -> assertThat("value for key " + key,resultSet.get(key), is(value)));
38 }
39 executables.add(() -> assertThat("results size", resultSet.size(), is(filteredExpected.size())));
40
41 var actual = new LinkedHashMap<Tuple, T>();
42 var cursor = resultSet.getAll();
43 while (cursor.move()) {
44 var key = cursor.getKey();
45 var previous = actual.put(key.toTuple(), cursor.getValue());
46 assertThat("duplicate value for key " + key, previous, nullValue());
47 }
48 executables.add(() -> assertThat("results cursor", actual, is(filteredExpected)));
49
50 assertAll(executables);
51 }
52}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java
new file mode 100644
index 00000000..b1818a17
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryBackendHint.java
@@ -0,0 +1,22 @@
1package tools.refinery.store.query.viatra.tests;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4
5/**
6 * Overrides {@link QueryEvaluationHint#toString()} for pretty names in parametric test names.
7 */
8class QueryBackendHint extends QueryEvaluationHint {
9 public QueryBackendHint(BackendRequirement backendRequirementType) {
10 super(null, backendRequirementType);
11 }
12
13 @Override
14 public String toString() {
15 return switch (getQueryBackendRequirementType()) {
16 case UNSPECIFIED -> "default";
17 case DEFAULT_CACHING -> "incremental";
18 case DEFAULT_SEARCH -> "localSearch";
19 default -> throw new IllegalStateException("Unknown BackendRequirement");
20 };
21 }
22}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java
new file mode 100644
index 00000000..f129520c
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEngineTest.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.viatra.tests;
2
3import org.junit.jupiter.params.ParameterizedTest;
4import org.junit.jupiter.params.provider.ArgumentsSource;
5
6import java.lang.annotation.ElementType;
7import java.lang.annotation.Retention;
8import java.lang.annotation.RetentionPolicy;
9import java.lang.annotation.Target;
10
11@ParameterizedTest(name = "backend = {0}")
12@ArgumentsSource(QueryEvaluationHintSource.class)
13@Target(ElementType.METHOD)
14@Retention(RetentionPolicy.RUNTIME)
15public @interface QueryEngineTest {
16}
diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java
new file mode 100644
index 00000000..a55762e2
--- /dev/null
+++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/tests/QueryEvaluationHintSource.java
@@ -0,0 +1,19 @@
1package tools.refinery.store.query.viatra.tests;
2
3import org.eclipse.viatra.query.runtime.matchers.backend.QueryEvaluationHint;
4import org.junit.jupiter.api.extension.ExtensionContext;
5import org.junit.jupiter.params.provider.Arguments;
6import org.junit.jupiter.params.provider.ArgumentsProvider;
7
8import java.util.stream.Stream;
9
10public class QueryEvaluationHintSource implements ArgumentsProvider {
11 @Override
12 public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
13 return Stream.of(
14 Arguments.of(new QueryBackendHint(QueryEvaluationHint.BackendRequirement.UNSPECIFIED)),
15 Arguments.of(new QueryBackendHint(QueryEvaluationHint.BackendRequirement.DEFAULT_CACHING)),
16 Arguments.of(new QueryBackendHint(QueryEvaluationHint.BackendRequirement.DEFAULT_SEARCH))
17 );
18 }
19}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/AnyResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/AnyResultSet.java
new file mode 100644
index 00000000..6d411212
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/AnyResultSet.java
@@ -0,0 +1,11 @@
1package tools.refinery.store.query;
2
3import tools.refinery.store.query.dnf.AnyQuery;
4
5public sealed interface AnyResultSet permits ResultSet {
6 ModelQueryAdapter getAdapter();
7
8 AnyQuery getQuery();
9
10 int size();
11}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java
new file mode 100644
index 00000000..cec4c19f
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/Constraint.java
@@ -0,0 +1,65 @@
1package tools.refinery.store.query;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.literal.*;
5import tools.refinery.store.query.term.*;
6
7import java.util.List;
8
9public interface Constraint {
10 String name();
11
12 List<Sort> getSorts();
13
14 default int arity() {
15 return getSorts().size();
16 }
17
18 default boolean invalidIndex(int i) {
19 return i < 0 || i >= arity();
20 }
21
22 default LiteralReduction getReduction() {
23 return LiteralReduction.NOT_REDUCIBLE;
24 }
25
26 default boolean equals(LiteralEqualityHelper helper, Constraint other) {
27 return equals(other);
28 }
29
30 String toReferenceString();
31
32 default CallLiteral call(CallPolarity polarity, List<Variable> arguments) {
33 return new CallLiteral(polarity, this, arguments);
34 }
35
36 default CallLiteral call(CallPolarity polarity, Variable... arguments) {
37 return call(polarity, List.of(arguments));
38 }
39
40 default CallLiteral call(Variable... arguments) {
41 return call(CallPolarity.POSITIVE, arguments);
42 }
43
44 default CallLiteral callTransitive(NodeVariable left, NodeVariable right) {
45 return call(CallPolarity.TRANSITIVE, List.of(left, right));
46 }
47
48 default AssignedValue<Integer> count(List<Variable> arguments) {
49 return targetVariable -> new CountLiteral(targetVariable, this, arguments);
50 }
51
52 default AssignedValue<Integer> count(Variable... arguments) {
53 return count(List.of(arguments));
54 }
55
56 default <R, T> AssignedValue<R> aggregate(DataVariable<T> inputVariable, Aggregator<R, T> aggregator,
57 List<Variable> arguments) {
58 return targetVariable -> new AggregationLiteral<>(targetVariable, aggregator, inputVariable, this, arguments);
59 }
60
61 default <R, T> AssignedValue<R> aggregate(DataVariable<T> inputVariable, Aggregator<R, T> aggregator,
62 Variable... arguments) {
63 return aggregate(inputVariable, aggregator, List.of(arguments));
64 }
65}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java
index 9ff6df26..9af73bdd 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/EmptyResultSet.java
@@ -2,21 +2,33 @@ package tools.refinery.store.query;
2 2
3import tools.refinery.store.map.Cursor; 3import tools.refinery.store.map.Cursor;
4import tools.refinery.store.map.Cursors; 4import tools.refinery.store.map.Cursors;
5import tools.refinery.store.query.dnf.Query;
5import tools.refinery.store.tuple.TupleLike; 6import tools.refinery.store.tuple.TupleLike;
6 7
7public class EmptyResultSet implements ResultSet { 8public record EmptyResultSet<T>(ModelQueryAdapter adapter, Query<T> query) implements ResultSet<T> {
8 @Override 9 @Override
9 public boolean hasResult(TupleLike parameters) { 10 public ModelQueryAdapter getAdapter() {
10 return false; 11 return adapter;
11 } 12 }
12 13
13 @Override 14 @Override
14 public Cursor<TupleLike, Boolean> allResults() { 15 public Query<T> getQuery() {
16 return query;
17 }
18
19 @Override
20 public T get(TupleLike parameters) {
21 return query.defaultValue();
22 }
23
24
25 @Override
26 public Cursor<TupleLike, T> getAll() {
15 return Cursors.empty(); 27 return Cursors.empty();
16 } 28 }
17 29
18 @Override 30 @Override
19 public int countResults() { 31 public int size() {
20 return 0; 32 return 0;
21 } 33 }
22} 34}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryAdapter.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryAdapter.java
index f7762444..2e30fec4 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryAdapter.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryAdapter.java
@@ -1,11 +1,17 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query;
2 2
3import tools.refinery.store.adapter.ModelAdapter; 3import tools.refinery.store.adapter.ModelAdapter;
4import tools.refinery.store.query.dnf.AnyQuery;
5import tools.refinery.store.query.dnf.Query;
4 6
5public interface ModelQueryAdapter extends ModelAdapter { 7public interface ModelQueryAdapter extends ModelAdapter {
6 ModelQueryStoreAdapter getStoreAdapter(); 8 ModelQueryStoreAdapter getStoreAdapter();
7 9
8 ResultSet getResultSet(Dnf query); 10 default AnyResultSet getResultSet(AnyQuery query) {
11 return getResultSet((Query<?>) query);
12 }
13
14 <T> ResultSet<T> getResultSet(Query<T> query);
9 15
10 boolean hasPendingChanges(); 16 boolean hasPendingChanges();
11 17
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java
index b3cfb4b4..4fdc9210 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryBuilder.java
@@ -2,21 +2,23 @@ package tools.refinery.store.query;
2 2
3import tools.refinery.store.adapter.ModelAdapterBuilder; 3import tools.refinery.store.adapter.ModelAdapterBuilder;
4import tools.refinery.store.model.ModelStore; 4import tools.refinery.store.model.ModelStore;
5import tools.refinery.store.query.dnf.AnyQuery;
5 6
6import java.util.Collection; 7import java.util.Collection;
7import java.util.List; 8import java.util.List;
8 9
10@SuppressWarnings("UnusedReturnValue")
9public interface ModelQueryBuilder extends ModelAdapterBuilder { 11public interface ModelQueryBuilder extends ModelAdapterBuilder {
10 default ModelQueryBuilder queries(Dnf... queries) { 12 default ModelQueryBuilder queries(AnyQuery... queries) {
11 return queries(List.of(queries)); 13 return queries(List.of(queries));
12 } 14 }
13 15
14 default ModelQueryBuilder queries(Collection<Dnf> queries) { 16 default ModelQueryBuilder queries(Collection<? extends AnyQuery> queries) {
15 queries.forEach(this::query); 17 queries.forEach(this::query);
16 return this; 18 return this;
17 } 19 }
18 20
19 ModelQueryBuilder query(Dnf query); 21 ModelQueryBuilder query(AnyQuery query);
20 22
21 @Override 23 @Override
22 ModelQueryStoreAdapter createStoreAdapter(ModelStore store); 24 ModelQueryStoreAdapter createStoreAdapter(ModelStore store);
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java
index 091d6d06..514e582b 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ModelQueryStoreAdapter.java
@@ -1,15 +1,16 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query;
2 2
3import tools.refinery.store.query.view.AnyRelationView;
4import tools.refinery.store.adapter.ModelStoreAdapter; 3import tools.refinery.store.adapter.ModelStoreAdapter;
5import tools.refinery.store.model.Model; 4import tools.refinery.store.model.Model;
5import tools.refinery.store.query.dnf.AnyQuery;
6import tools.refinery.store.query.view.AnyRelationView;
6 7
7import java.util.Collection; 8import java.util.Collection;
8 9
9public interface ModelQueryStoreAdapter extends ModelStoreAdapter { 10public interface ModelQueryStoreAdapter extends ModelStoreAdapter {
10 Collection<AnyRelationView> getRelationViews(); 11 Collection<AnyRelationView> getRelationViews();
11 12
12 Collection<Dnf> getQueries(); 13 Collection<AnyQuery> getQueries();
13 14
14 @Override 15 @Override
15 ModelQueryAdapter createModelAdapter(Model model); 16 ModelQueryAdapter createModelAdapter(Model model);
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/RelationLike.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/RelationLike.java
deleted file mode 100644
index 8c784d8b..00000000
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/RelationLike.java
+++ /dev/null
@@ -1,11 +0,0 @@
1package tools.refinery.store.query;
2
3public interface RelationLike {
4 String name();
5
6 int arity();
7
8 default boolean invalidIndex(int i) {
9 return i < 0 || i >= arity();
10 }
11}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java
index d2b8c9dd..3f6bc06f 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/ResultSet.java
@@ -1,16 +1,13 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query;
2 2
3import tools.refinery.store.map.Cursor; 3import tools.refinery.store.map.Cursor;
4import tools.refinery.store.query.dnf.Query;
4import tools.refinery.store.tuple.TupleLike; 5import tools.refinery.store.tuple.TupleLike;
5 6
6public interface ResultSet { 7public non-sealed interface ResultSet<T> extends AnyResultSet {
7 default boolean hasResult() { 8 Query<T> getQuery();
8 return countResults() > 0;
9 }
10 9
11 boolean hasResult(TupleLike parameters); 10 T get(TupleLike parameters);
12 11
13 Cursor<TupleLike, Boolean> allResults(); 12 Cursor<TupleLike, T> getAll();
14
15 int countResults();
16} 13}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/Variable.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/Variable.java
deleted file mode 100644
index d0e0dead..00000000
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/Variable.java
+++ /dev/null
@@ -1,63 +0,0 @@
1package tools.refinery.store.query;
2
3import tools.refinery.store.query.literal.ConstantLiteral;
4import tools.refinery.store.query.literal.EquivalenceLiteral;
5
6import java.util.Objects;
7
8public class Variable {
9 private final String name;
10 private final String uniqueName;
11
12 public Variable() {
13 this(null);
14 }
15
16 public Variable(String name) {
17 super();
18 this.name = name;
19 this.uniqueName = DnfUtils.generateUniqueName(name);
20
21 }
22 public String getName() {
23 return name == null ? uniqueName : name;
24 }
25
26 public boolean isExplicitlyNamed() {
27 return name != null;
28 }
29
30 public String getUniqueName() {
31 return uniqueName;
32 }
33
34 public ConstantLiteral isConstant(int value) {
35 return new ConstantLiteral(this, value);
36 }
37
38 public EquivalenceLiteral isEquivalent(Variable other) {
39 return new EquivalenceLiteral(true, this, other);
40 }
41
42 public EquivalenceLiteral notEquivalent(Variable other) {
43 return new EquivalenceLiteral(false, this, other);
44 }
45
46 @Override
47 public String toString() {
48 return getName();
49 }
50
51 @Override
52 public boolean equals(Object o) {
53 if (this == o) return true;
54 if (o == null || getClass() != o.getClass()) return false;
55 Variable variable = (Variable) o;
56 return Objects.equals(uniqueName, variable.uniqueName);
57 }
58
59 @Override
60 public int hashCode() {
61 return Objects.hash(uniqueName);
62 }
63}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/AnyQuery.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/AnyQuery.java
new file mode 100644
index 00000000..d0a2367f
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/AnyQuery.java
@@ -0,0 +1,11 @@
1package tools.refinery.store.query.dnf;
2
3public sealed interface AnyQuery permits Query {
4 String name();
5
6 int arity();
7
8 Class<?> valueType();
9
10 Dnf getDnf();
11}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/Dnf.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Dnf.java
index b6744b50..1b7759c7 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/Dnf.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Dnf.java
@@ -1,14 +1,18 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.dnf;
2 2
3import tools.refinery.store.query.equality.DnfEqualityChecker; 3import tools.refinery.store.query.equality.DnfEqualityChecker;
4import tools.refinery.store.query.equality.LiteralEqualityHelper; 4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.literal.CallPolarity; 5import tools.refinery.store.query.Constraint;
6import tools.refinery.store.query.literal.DnfCallLiteral;
7import tools.refinery.store.query.literal.LiteralReduction; 6import tools.refinery.store.query.literal.LiteralReduction;
7import tools.refinery.store.query.term.Sort;
8import tools.refinery.store.query.term.Variable;
8 9
9import java.util.*; 10import java.util.Collection;
11import java.util.HashSet;
12import java.util.List;
13import java.util.Set;
10 14
11public final class Dnf implements RelationLike { 15public final class Dnf implements Constraint {
12 private static final String INDENTATION = " "; 16 private static final String INDENTATION = " ";
13 17
14 private final String name; 18 private final String name;
@@ -69,6 +73,11 @@ public final class Dnf implements RelationLike {
69 return parameters; 73 return parameters;
70 } 74 }
71 75
76 @Override
77 public List<Sort> getSorts() {
78 return parameters.stream().map(Variable::getSort).toList();
79 }
80
72 public List<FunctionalDependency<Variable>> getFunctionalDependencies() { 81 public List<FunctionalDependency<Variable>> getFunctionalDependencies() {
73 return functionalDependencies; 82 return functionalDependencies;
74 } 83 }
@@ -82,6 +91,15 @@ public final class Dnf implements RelationLike {
82 return clauses; 91 return clauses;
83 } 92 }
84 93
94 public RelationalQuery asRelation() {
95 return new RelationalQuery(this);
96 }
97
98 public <T> FunctionalQuery<T> asFunction(Class<T> type) {
99 return new FunctionalQuery<>(this, type);
100 }
101
102 @Override
85 public LiteralReduction getReduction() { 103 public LiteralReduction getReduction() {
86 if (clauses.isEmpty()) { 104 if (clauses.isEmpty()) {
87 return LiteralReduction.ALWAYS_FALSE; 105 return LiteralReduction.ALWAYS_FALSE;
@@ -94,22 +112,6 @@ public final class Dnf implements RelationLike {
94 return LiteralReduction.NOT_REDUCIBLE; 112 return LiteralReduction.NOT_REDUCIBLE;
95 } 113 }
96 114
97 public DnfCallLiteral call(CallPolarity polarity, List<Variable> arguments) {
98 return new DnfCallLiteral(polarity, this, arguments);
99 }
100
101 public DnfCallLiteral call(CallPolarity polarity, Variable... arguments) {
102 return call(polarity, List.of(arguments));
103 }
104
105 public DnfCallLiteral call(Variable... arguments) {
106 return call(CallPolarity.POSITIVE, arguments);
107 }
108
109 public DnfCallLiteral callTransitive(Variable left, Variable right) {
110 return call(CallPolarity.TRANSITIVE, List.of(left, right));
111 }
112
113 public boolean equalsWithSubstitution(DnfEqualityChecker callEqualityChecker, Dnf other) { 115 public boolean equalsWithSubstitution(DnfEqualityChecker callEqualityChecker, Dnf other) {
114 if (arity() != other.arity()) { 116 if (arity() != other.arity()) {
115 return false; 117 return false;
@@ -128,7 +130,24 @@ public final class Dnf implements RelationLike {
128 } 130 }
129 131
130 @Override 132 @Override
133 public boolean equals(LiteralEqualityHelper helper, Constraint other) {
134 if (other instanceof Dnf otherDnf) {
135 return helper.dnfEqual(this, otherDnf);
136 }
137 return false;
138 }
139
140 @Override
131 public String toString() { 141 public String toString() {
142 return "%s/%d".formatted(name, arity());
143 }
144
145 @Override
146 public String toReferenceString() {
147 return "@Dnf " + name;
148 }
149
150 public String toDefinitionString() {
132 var builder = new StringBuilder(); 151 var builder = new StringBuilder();
133 builder.append("pred ").append(name()).append("("); 152 builder.append("pred ").append(name()).append("(");
134 var parameterIterator = parameters.iterator(); 153 var parameterIterator = parameters.iterator();
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfBuilder.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfBuilder.java
index ca47e979..aad5a85f 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfBuilder.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfBuilder.java
@@ -1,11 +1,13 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.dnf;
2 2
3import tools.refinery.store.query.literal.Literal; 3import tools.refinery.store.query.literal.Literal;
4import tools.refinery.store.query.term.DataVariable;
5import tools.refinery.store.query.term.Variable;
4 6
5import java.util.*; 7import java.util.*;
6 8
7@SuppressWarnings("UnusedReturnValue") 9@SuppressWarnings("UnusedReturnValue")
8public class DnfBuilder { 10public final class DnfBuilder {
9 private final String name; 11 private final String name;
10 12
11 private final List<Variable> parameters = new ArrayList<>(); 13 private final List<Variable> parameters = new ArrayList<>();
@@ -19,6 +21,9 @@ public class DnfBuilder {
19 } 21 }
20 22
21 public DnfBuilder parameter(Variable variable) { 23 public DnfBuilder parameter(Variable variable) {
24 if (parameters.contains(variable)) {
25 throw new IllegalArgumentException("Duplicate parameter: " + variable);
26 }
22 parameters.add(variable); 27 parameters.add(variable);
23 return this; 28 return this;
24 } 29 }
@@ -27,7 +32,7 @@ public class DnfBuilder {
27 return parameters(List.of(variables)); 32 return parameters(List.of(variables));
28 } 33 }
29 34
30 public DnfBuilder parameters(Collection<Variable> variables) { 35 public DnfBuilder parameters(Collection<? extends Variable> variables) {
31 parameters.addAll(variables); 36 parameters.addAll(variables);
32 return this; 37 return this;
33 } 38 }
@@ -42,8 +47,8 @@ public class DnfBuilder {
42 return this; 47 return this;
43 } 48 }
44 49
45 public DnfBuilder functionalDependency(Set<Variable> forEach, Set<Variable> unique) { 50 public DnfBuilder functionalDependency(Set<? extends Variable> forEach, Set<? extends Variable> unique) {
46 return functionalDependency(new FunctionalDependency<>(forEach, unique)); 51 return functionalDependency(new FunctionalDependency<>(Set.copyOf(forEach), Set.copyOf(unique)));
47 } 52 }
48 53
49 public DnfBuilder clause(Literal... literals) { 54 public DnfBuilder clause(Literal... literals) {
@@ -51,7 +56,7 @@ public class DnfBuilder {
51 return this; 56 return this;
52 } 57 }
53 58
54 public DnfBuilder clause(Collection<Literal> literals) { 59 public DnfBuilder clause(Collection<? extends Literal> literals) {
55 // Remove duplicates by using a hashed data structure. 60 // Remove duplicates by using a hashed data structure.
56 var filteredLiterals = new LinkedHashSet<Literal>(literals.size()); 61 var filteredLiterals = new LinkedHashSet<Literal>(literals.size());
57 for (var literal : literals) { 62 for (var literal : literals) {
@@ -73,21 +78,6 @@ public class DnfBuilder {
73 return this; 78 return this;
74 } 79 }
75 80
76 public DnfBuilder clause(DnfClause clause) {
77 return clause(clause.literals());
78 }
79
80 public DnfBuilder clauses(DnfClause... clauses) {
81 return clauses(List.of(clauses));
82 }
83
84 public DnfBuilder clauses(Collection<DnfClause> clauses) {
85 for (var clause : clauses) {
86 this.clause(clause);
87 }
88 return this;
89 }
90
91 public Dnf build() { 81 public Dnf build() {
92 var postProcessedClauses = postProcessClauses(); 82 var postProcessedClauses = postProcessClauses();
93 return new Dnf(name, Collections.unmodifiableList(parameters), 83 return new Dnf(name, Collections.unmodifiableList(parameters),
@@ -95,6 +85,11 @@ public class DnfBuilder {
95 Collections.unmodifiableList(postProcessedClauses)); 85 Collections.unmodifiableList(postProcessedClauses));
96 } 86 }
97 87
88 <T> void output(DataVariable<T> outputVariable) {
89 functionalDependency(Set.copyOf(parameters), Set.of(outputVariable));
90 parameter(outputVariable);
91 }
92
98 private List<DnfClause> postProcessClauses() { 93 private List<DnfClause> postProcessClauses() {
99 var postProcessedClauses = new ArrayList<DnfClause>(clauses.size()); 94 var postProcessedClauses = new ArrayList<DnfClause>(clauses.size());
100 for (var literals : clauses) { 95 for (var literals : clauses) {
@@ -103,8 +98,8 @@ public class DnfBuilder {
103 return List.of(new DnfClause(Set.of(), List.of())); 98 return List.of(new DnfClause(Set.of(), List.of()));
104 } 99 }
105 var variables = new HashSet<Variable>(); 100 var variables = new HashSet<Variable>();
106 for (var constraint : literals) { 101 for (var literal : literals) {
107 constraint.collectAllVariables(variables); 102 variables.addAll(literal.getBoundVariables());
108 } 103 }
109 parameters.forEach(variables::remove); 104 parameters.forEach(variables::remove);
110 postProcessedClauses.add(new DnfClause(Collections.unmodifiableSet(variables), 105 postProcessedClauses.add(new DnfClause(Collections.unmodifiableSet(variables),
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfClause.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfClause.java
index c6e8b8c9..01830af1 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfClause.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfClause.java
@@ -1,12 +1,13 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.dnf;
2 2
3import tools.refinery.store.query.equality.LiteralEqualityHelper; 3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.literal.Literal; 4import tools.refinery.store.query.literal.Literal;
5import tools.refinery.store.query.term.Variable;
5 6
6import java.util.List; 7import java.util.List;
7import java.util.Set; 8import java.util.Set;
8 9
9public record DnfClause(Set<Variable> quantifiedVariables, List<Literal> literals) { 10public record DnfClause(Set<Variable> boundVariables, List<Literal> literals) {
10 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, DnfClause other) { 11 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, DnfClause other) {
11 int size = literals.size(); 12 int size = literals.size();
12 if (size != other.literals.size()) { 13 if (size != other.literals.size()) {
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfUtils.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfUtils.java
index c7a2849c..9bcf944c 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/DnfUtils.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/DnfUtils.java
@@ -1,4 +1,4 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.dnf;
2 2
3import java.util.UUID; 3import java.util.UUID;
4 4
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/FunctionalDependency.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalDependency.java
index 63a81713..f4cd109f 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/FunctionalDependency.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalDependency.java
@@ -1,4 +1,4 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query.dnf;
2 2
3import java.util.HashSet; 3import java.util.HashSet;
4import java.util.Set; 4import java.util.Set;
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java
new file mode 100644
index 00000000..5bf6f8c5
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQuery.java
@@ -0,0 +1,103 @@
1package tools.refinery.store.query.dnf;
2
3import tools.refinery.store.query.literal.CallPolarity;
4import tools.refinery.store.query.term.*;
5
6import java.util.ArrayList;
7import java.util.List;
8import java.util.Objects;
9
10public final class FunctionalQuery<T> implements Query<T> {
11 private final Dnf dnf;
12 private final Class<T> type;
13
14 FunctionalQuery(Dnf dnf, Class<T> type) {
15 var parameters = dnf.getParameters();
16 int outputIndex = dnf.arity() - 1;
17 for (int i = 0; i < outputIndex; i++) {
18 var parameter = parameters.get(i);
19 if (!(parameter instanceof NodeVariable)) {
20 throw new IllegalArgumentException("Expected parameter %s of %s to be of sort %s, but got %s instead"
21 .formatted(parameter, dnf, NodeSort.INSTANCE, parameter.getSort()));
22 }
23 }
24 var outputParameter = parameters.get(outputIndex);
25 if (!(outputParameter instanceof DataVariable<?> dataOutputParameter) ||
26 !dataOutputParameter.getType().equals(type)) {
27 throw new IllegalArgumentException("Expected parameter %s of %s to be of sort %s, but got %s instead"
28 .formatted(outputParameter, dnf, type, outputParameter.getSort()));
29 }
30 this.dnf = dnf;
31 this.type = type;
32 }
33
34 @Override
35 public String name() {
36 return dnf.name();
37 }
38
39 @Override
40 public int arity() {
41 return dnf.arity() - 1;
42 }
43
44 @Override
45 public Class<T> valueType() {
46 return type;
47 }
48
49 @Override
50 public T defaultValue() {
51 return null;
52 }
53
54 @Override
55 public Dnf getDnf() {
56 return dnf;
57 }
58
59 public AssignedValue<T> call(List<NodeVariable> arguments) {
60 return targetVariable -> {
61 var argumentsWithTarget = new ArrayList<Variable>(arguments.size() + 1);
62 argumentsWithTarget.addAll(arguments);
63 argumentsWithTarget.add(targetVariable);
64 return dnf.call(CallPolarity.POSITIVE, argumentsWithTarget);
65 };
66 }
67
68 public AssignedValue<T> call(NodeVariable... arguments) {
69 return call(List.of(arguments));
70 }
71
72 public <R> AssignedValue<R> aggregate(Aggregator<R, T> aggregator, List<NodeVariable> arguments) {
73 return targetVariable -> {
74 var placeholderVariable = Variable.of(type);
75 var argumentsWithPlaceholder = new ArrayList<Variable>(arguments.size() + 1);
76 argumentsWithPlaceholder.addAll(arguments);
77 argumentsWithPlaceholder.add(placeholderVariable);
78 return dnf.aggregate(placeholderVariable, aggregator, argumentsWithPlaceholder).toLiteral(targetVariable);
79 };
80 }
81
82 public <R> AssignedValue<R> aggregate(Aggregator<R, T> aggregator, NodeVariable... arguments) {
83 return aggregate(aggregator, List.of(arguments));
84 }
85
86 @Override
87 public boolean equals(Object o) {
88 if (this == o) return true;
89 if (o == null || getClass() != o.getClass()) return false;
90 FunctionalQuery<?> that = (FunctionalQuery<?>) o;
91 return dnf.equals(that.dnf) && type.equals(that.type);
92 }
93
94 @Override
95 public int hashCode() {
96 return Objects.hash(dnf, type);
97 }
98
99 @Override
100 public String toString() {
101 return dnf.toString();
102 }
103}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQueryBuilder.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQueryBuilder.java
new file mode 100644
index 00000000..ca2bc006
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/FunctionalQueryBuilder.java
@@ -0,0 +1,46 @@
1package tools.refinery.store.query.dnf;
2
3import tools.refinery.store.query.literal.Literal;
4import tools.refinery.store.query.term.Variable;
5
6import java.util.Collection;
7import java.util.Set;
8
9public final class FunctionalQueryBuilder<T> {
10 private final DnfBuilder dnfBuilder;
11 private final Class<T> type;
12
13 FunctionalQueryBuilder(DnfBuilder dnfBuilder, Class<T> type) {
14 this.dnfBuilder = dnfBuilder;
15 this.type = type;
16 }
17
18 public FunctionalQueryBuilder<T> functionalDependencies(Collection<FunctionalDependency<Variable>> functionalDependencies) {
19 dnfBuilder.functionalDependencies(functionalDependencies);
20 return this;
21 }
22
23 public FunctionalQueryBuilder<T> functionalDependency(FunctionalDependency<Variable> functionalDependency) {
24 dnfBuilder.functionalDependency(functionalDependency);
25 return this;
26 }
27
28 public FunctionalQueryBuilder<T> functionalDependency(Set<? extends Variable> forEach, Set<? extends Variable> unique) {
29 dnfBuilder.functionalDependency(forEach, unique);
30 return this;
31 }
32
33 public FunctionalQueryBuilder<T> clause(Literal... literals) {
34 dnfBuilder.clause(literals);
35 return this;
36 }
37
38 public FunctionalQueryBuilder<T> clause(Collection<? extends Literal> literals) {
39 dnfBuilder.clause(literals);
40 return this;
41 }
42
43 public FunctionalQuery<T> build() {
44 return dnfBuilder.build().asFunction(type);
45 }
46}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java
new file mode 100644
index 00000000..32e33052
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/Query.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.dnf;
2
3public sealed interface Query<T> extends AnyQuery permits RelationalQuery, FunctionalQuery {
4 @Override
5 Class<T> valueType();
6
7 T defaultValue();
8
9 static QueryBuilder builder() {
10 return new QueryBuilder();
11 }
12
13 static QueryBuilder builder(String name) {
14 return new QueryBuilder(name);
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/QueryBuilder.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/QueryBuilder.java
new file mode 100644
index 00000000..ed253cc9
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/QueryBuilder.java
@@ -0,0 +1,71 @@
1package tools.refinery.store.query.dnf;
2
3import tools.refinery.store.query.literal.Literal;
4import tools.refinery.store.query.term.DataVariable;
5import tools.refinery.store.query.term.NodeVariable;
6import tools.refinery.store.query.term.Variable;
7
8import java.util.Collection;
9import java.util.List;
10import java.util.Set;
11
12public final class QueryBuilder {
13 private final DnfBuilder dnfBuilder;
14
15 QueryBuilder(String name) {
16 dnfBuilder = Dnf.builder(name);
17 }
18
19 QueryBuilder() {
20 dnfBuilder = Dnf.builder();
21 }
22
23 public QueryBuilder parameter(NodeVariable variable) {
24 dnfBuilder.parameter(variable);
25 return this;
26 }
27
28 public QueryBuilder parameters(NodeVariable... variables) {
29 dnfBuilder.parameters(variables);
30 return this;
31 }
32
33 public QueryBuilder parameters(List<NodeVariable> variables) {
34 dnfBuilder.parameters(variables);
35 return this;
36 }
37
38 public <T> FunctionalQueryBuilder<T> output(DataVariable<T> outputVariable) {
39 dnfBuilder.output(outputVariable);
40 return new FunctionalQueryBuilder<>(dnfBuilder, outputVariable.getType());
41 }
42
43 public QueryBuilder functionalDependencies(Collection<FunctionalDependency<Variable>> functionalDependencies) {
44 dnfBuilder.functionalDependencies(functionalDependencies);
45 return this;
46 }
47
48 public QueryBuilder functionalDependency(FunctionalDependency<Variable> functionalDependency) {
49 dnfBuilder.functionalDependency(functionalDependency);
50 return this;
51 }
52
53 public QueryBuilder functionalDependency(Set<? extends Variable> forEach, Set<? extends Variable> unique) {
54 dnfBuilder.functionalDependency(forEach, unique);
55 return this;
56 }
57
58 public QueryBuilder clause(Literal... literals) {
59 dnfBuilder.clause(literals);
60 return this;
61 }
62
63 public QueryBuilder clause(Collection<? extends Literal> literals) {
64 dnfBuilder.clause(literals);
65 return this;
66 }
67
68 public RelationalQuery build() {
69 return dnfBuilder.build().asRelation();
70 }
71}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/RelationalQuery.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/RelationalQuery.java
new file mode 100644
index 00000000..5307e509
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/dnf/RelationalQuery.java
@@ -0,0 +1,93 @@
1package tools.refinery.store.query.dnf;
2
3import tools.refinery.store.query.literal.CallLiteral;
4import tools.refinery.store.query.literal.CallPolarity;
5import tools.refinery.store.query.term.AssignedValue;
6import tools.refinery.store.query.term.NodeSort;
7import tools.refinery.store.query.term.NodeVariable;
8
9import java.util.Collections;
10import java.util.List;
11import java.util.Objects;
12
13public final class RelationalQuery implements Query<Boolean> {
14 private final Dnf dnf;
15
16 RelationalQuery(Dnf dnf) {
17 for (var parameter : dnf.getParameters()) {
18 if (!(parameter instanceof NodeVariable)) {
19 throw new IllegalArgumentException("Expected parameter %s of %s to be of sort %s, but got %s instead"
20 .formatted(parameter, dnf, NodeSort.INSTANCE, parameter.getSort()));
21 }
22 }
23 this.dnf = dnf;
24 }
25
26 @Override
27 public String name() {
28 return dnf.name();
29 }
30
31 @Override
32 public int arity() {
33 return dnf.arity();
34 }
35
36 @Override
37 public Class<Boolean> valueType() {
38 return Boolean.class;
39 }
40
41 @Override
42 public Boolean defaultValue() {
43 return false;
44 }
45
46 @Override
47 public Dnf getDnf() {
48 return dnf;
49 }
50
51 public CallLiteral call(CallPolarity polarity, List<NodeVariable> arguments) {
52 return dnf.call(polarity, Collections.unmodifiableList(arguments));
53 }
54
55 public CallLiteral call(CallPolarity polarity, NodeVariable... arguments) {
56 return dnf.call(polarity, arguments);
57 }
58
59 public CallLiteral call(NodeVariable... arguments) {
60 return dnf.call(arguments);
61 }
62
63 public CallLiteral callTransitive(NodeVariable left, NodeVariable right) {
64 return dnf.callTransitive(left, right);
65 }
66
67 public AssignedValue<Integer> count(List<NodeVariable> arguments) {
68 return dnf.count(Collections.unmodifiableList(arguments));
69 }
70
71 public AssignedValue<Integer> count(NodeVariable... arguments) {
72 return dnf.count(arguments);
73 }
74
75
76 @Override
77 public boolean equals(Object o) {
78 if (this == o) return true;
79 if (o == null || getClass() != o.getClass()) return false;
80 RelationalQuery that = (RelationalQuery) o;
81 return dnf.equals(that.dnf);
82 }
83
84 @Override
85 public int hashCode() {
86 return Objects.hash(dnf);
87 }
88
89 @Override
90 public String toString() {
91 return dnf.toString();
92 }
93}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DeepDnfEqualityChecker.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DeepDnfEqualityChecker.java
index ebd7f5b0..c3bc3ea3 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DeepDnfEqualityChecker.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DeepDnfEqualityChecker.java
@@ -1,6 +1,6 @@
1package tools.refinery.store.query.equality; 1package tools.refinery.store.query.equality;
2 2
3import tools.refinery.store.query.Dnf; 3import tools.refinery.store.query.dnf.Dnf;
4import tools.refinery.store.util.CycleDetectingMapper; 4import tools.refinery.store.util.CycleDetectingMapper;
5 5
6import java.util.List; 6import java.util.List;
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DnfEqualityChecker.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DnfEqualityChecker.java
index eb77de17..6b1f2076 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DnfEqualityChecker.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/DnfEqualityChecker.java
@@ -1,6 +1,6 @@
1package tools.refinery.store.query.equality; 1package tools.refinery.store.query.equality;
2 2
3import tools.refinery.store.query.Dnf; 3import tools.refinery.store.query.dnf.Dnf;
4 4
5@FunctionalInterface 5@FunctionalInterface
6public interface DnfEqualityChecker { 6public interface DnfEqualityChecker {
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/LiteralEqualityHelper.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/LiteralEqualityHelper.java
index 23f1acc7..07d261ea 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/LiteralEqualityHelper.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/equality/LiteralEqualityHelper.java
@@ -1,7 +1,7 @@
1package tools.refinery.store.query.equality; 1package tools.refinery.store.query.equality;
2 2
3import tools.refinery.store.query.Dnf; 3import tools.refinery.store.query.dnf.Dnf;
4import tools.refinery.store.query.Variable; 4import tools.refinery.store.query.term.Variable;
5 5
6import java.util.HashMap; 6import java.util.HashMap;
7import java.util.List; 7import java.util.List;
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java
new file mode 100644
index 00000000..657ca26b
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AbstractCallLiteral.java
@@ -0,0 +1,80 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.Constraint;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution;
6import tools.refinery.store.query.term.Variable;
7
8import java.util.List;
9import java.util.Objects;
10
11public abstract class AbstractCallLiteral implements Literal {
12 private final Constraint target;
13 private final List<Variable> arguments;
14
15 protected AbstractCallLiteral(Constraint target, List<Variable> arguments) {
16 int arity = target.arity();
17 if (arguments.size() != arity) {
18 throw new IllegalArgumentException("%s needs %d arguments, but got %s".formatted(target.name(),
19 target.arity(), arguments.size()));
20 }
21 this.target = target;
22 this.arguments = arguments;
23 var sorts = target.getSorts();
24 for (int i = 0; i < arity; i++) {
25 var argument = arguments.get(i);
26 var sort = sorts.get(i);
27 if (!sort.isInstance(argument)) {
28 throw new IllegalArgumentException("Required argument %d of %s to be of sort %s, but got %s instead"
29 .formatted(i, target, sort, argument.getSort()));
30 }
31 }
32 }
33
34 public Constraint getTarget() {
35 return target;
36 }
37
38 public List<Variable> getArguments() {
39 return arguments;
40 }
41
42 @Override
43 public Literal substitute(Substitution substitution) {
44 var substitutedArguments = arguments.stream().map(substitution::getSubstitute).toList();
45 return doSubstitute(substitution, substitutedArguments);
46 }
47
48 protected abstract Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments);
49
50 @Override
51 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
52 if (other == null || getClass() != other.getClass()) {
53 return false;
54 }
55 var otherCallLiteral = (AbstractCallLiteral) other;
56 var arity = arguments.size();
57 if (arity != otherCallLiteral.arguments.size()) {
58 return false;
59 }
60 for (int i = 0; i < arity; i++) {
61 if (!helper.variableEqual(arguments.get(i), otherCallLiteral.arguments.get(i))) {
62 return false;
63 }
64 }
65 return target.equals(helper, otherCallLiteral.target);
66 }
67
68 @Override
69 public boolean equals(Object o) {
70 if (this == o) return true;
71 if (o == null || getClass() != o.getClass()) return false;
72 AbstractCallLiteral that = (AbstractCallLiteral) o;
73 return target.equals(that.target) && arguments.equals(that.arguments);
74 }
75
76 @Override
77 public int hashCode() {
78 return Objects.hash(target, arguments);
79 }
80}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java
new file mode 100644
index 00000000..df64839c
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AggregationLiteral.java
@@ -0,0 +1,113 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.Constraint;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution;
6import tools.refinery.store.query.term.Aggregator;
7import tools.refinery.store.query.term.DataVariable;
8import tools.refinery.store.query.term.Variable;
9
10import java.util.List;
11import java.util.Objects;
12import java.util.Set;
13
14public class AggregationLiteral<R, T> extends AbstractCallLiteral {
15 private final DataVariable<R> resultVariable;
16 private final DataVariable<T> inputVariable;
17 private final Aggregator<R, T> aggregator;
18
19 public AggregationLiteral(DataVariable<R> resultVariable, Aggregator<R, T> aggregator,
20 DataVariable<T> inputVariable, Constraint target, List<Variable> arguments) {
21 super(target, arguments);
22 if (!inputVariable.getType().equals(aggregator.getInputType())) {
23 throw new IllegalArgumentException("Input variable %s must of type %s, got %s instead".formatted(
24 inputVariable, aggregator.getInputType().getName(), inputVariable.getType().getName()));
25 }
26 if (!resultVariable.getType().equals(aggregator.getResultType())) {
27 throw new IllegalArgumentException("Result variable %s must of type %s, got %s instead".formatted(
28 resultVariable, aggregator.getResultType().getName(), resultVariable.getType().getName()));
29 }
30 if (!arguments.contains(inputVariable)) {
31 throw new IllegalArgumentException("Input variable %s must appear in the argument list".formatted(
32 inputVariable));
33 }
34 if (arguments.contains(resultVariable)) {
35 throw new IllegalArgumentException("Result variable %s must not appear in the argument list".formatted(
36 resultVariable));
37 }
38 this.resultVariable = resultVariable;
39 this.inputVariable = inputVariable;
40 this.aggregator = aggregator;
41 }
42
43 public DataVariable<R> getResultVariable() {
44 return resultVariable;
45 }
46
47 public DataVariable<T> getInputVariable() {
48 return inputVariable;
49 }
50
51 public Aggregator<R, T> getAggregator() {
52 return aggregator;
53 }
54
55 @Override
56 public Set<Variable> getBoundVariables() {
57 return Set.of(resultVariable);
58 }
59
60 @Override
61 protected Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments) {
62 return new AggregationLiteral<>(substitution.getTypeSafeSubstitute(resultVariable), aggregator,
63 substitution.getTypeSafeSubstitute(inputVariable), getTarget(), substitutedArguments);
64 }
65
66 @Override
67 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
68 if (!super.equalsWithSubstitution(helper, other)) {
69 return false;
70 }
71 var otherAggregationLiteral = (AggregationLiteral<?, ?>) other;
72 return helper.variableEqual(resultVariable, otherAggregationLiteral.resultVariable) &&
73 aggregator.equals(otherAggregationLiteral.aggregator) &&
74 helper.variableEqual(inputVariable, otherAggregationLiteral.inputVariable);
75 }
76
77 @Override
78 public boolean equals(Object o) {
79 if (this == o) return true;
80 if (o == null || getClass() != o.getClass()) return false;
81 if (!super.equals(o)) return false;
82 AggregationLiteral<?, ?> that = (AggregationLiteral<?, ?>) o;
83 return resultVariable.equals(that.resultVariable) && inputVariable.equals(that.inputVariable) &&
84 aggregator.equals(that.aggregator);
85 }
86
87 @Override
88 public int hashCode() {
89 return Objects.hash(super.hashCode(), resultVariable, inputVariable, aggregator);
90 }
91
92 @Override
93 public String toString() {
94 var builder = new StringBuilder();
95 builder.append(resultVariable);
96 builder.append(" is ");
97 builder.append(getTarget().toReferenceString());
98 builder.append("(");
99 var argumentIterator = getArguments().iterator();
100 if (argumentIterator.hasNext()) {
101 var argument = argumentIterator.next();
102 if (inputVariable.equals(argument)) {
103 builder.append("@Aggregate(\"").append(aggregator).append("\") ");
104 }
105 builder.append(argument);
106 while (argumentIterator.hasNext()) {
107 builder.append(", ").append(argumentIterator.next());
108 }
109 }
110 builder.append(")");
111 return builder.toString();
112 }
113}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssignLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssignLiteral.java
new file mode 100644
index 00000000..52ac42d7
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssignLiteral.java
@@ -0,0 +1,44 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.term.DataVariable;
6import tools.refinery.store.query.term.Term;
7import tools.refinery.store.query.term.Variable;
8
9import java.util.Set;
10
11public record AssignLiteral<T>(DataVariable<T> variable, Term<T> term) implements Literal {
12 public AssignLiteral {
13 if (!term.getType().equals(variable.getType())) {
14 throw new IllegalArgumentException("Term %s must be of type %s, got %s instead".formatted(
15 term, variable.getType().getName(), term.getType().getName()));
16 }
17 }
18
19 @Override
20 public Set<Variable> getBoundVariables() {
21 return Set.of(variable);
22 }
23
24 @Override
25 public Literal substitute(Substitution substitution) {
26 return new AssignLiteral<>(substitution.getTypeSafeSubstitute(variable), term.substitute(substitution));
27 }
28
29 @Override
30 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
31 if (other == null || getClass() != other.getClass()) {
32 return false;
33 }
34 var otherLetLiteral = (AssignLiteral<?>) other;
35 return helper.variableEqual(variable, otherLetLiteral.variable) && term.equalsWithSubstitution(helper,
36 otherLetLiteral.term);
37 }
38
39
40 @Override
41 public String toString() {
42 return "%s is (%s)".formatted(variable, term);
43 }
44}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssumeLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssumeLiteral.java
new file mode 100644
index 00000000..0b4267b4
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/AssumeLiteral.java
@@ -0,0 +1,53 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.term.Term;
6import tools.refinery.store.query.term.Variable;
7import tools.refinery.store.query.term.bool.BoolConstantTerm;
8
9import java.util.Set;
10
11public record AssumeLiteral(Term<Boolean> term) implements Literal {
12 public AssumeLiteral {
13 if (!term.getType().equals(Boolean.class)) {
14 throw new IllegalArgumentException("Term %s must be of type %s, got %s instead".formatted(
15 term, Boolean.class.getName(), term.getType().getName()));
16 }
17 }
18
19 @Override
20 public Set<Variable> getBoundVariables() {
21 return Set.of();
22 }
23
24 @Override
25 public Literal substitute(Substitution substitution) {
26 return new AssumeLiteral(term.substitute(substitution));
27 }
28
29 @Override
30 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
31 if (other == null || getClass() != other.getClass()) {
32 return false;
33 }
34 var otherAssumeLiteral = (AssumeLiteral) other;
35 return term.equalsWithSubstitution(helper, otherAssumeLiteral.term);
36 }
37
38 @Override
39 public LiteralReduction getReduction() {
40 if (BoolConstantTerm.TRUE.equals(term)) {
41 return LiteralReduction.ALWAYS_TRUE;
42 } else if (BoolConstantTerm.FALSE.equals(term)) {
43 return LiteralReduction.ALWAYS_FALSE;
44 } else {
45 return LiteralReduction.NOT_REDUCIBLE;
46 }
47 }
48
49 @Override
50 public String toString() {
51 return "(%s)".formatted(term);
52 }
53}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/BooleanLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/BooleanLiteral.java
index 6d751be8..38be61a4 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/BooleanLiteral.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/BooleanLiteral.java
@@ -1,12 +1,12 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.Variable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper; 4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution; 5import tools.refinery.store.query.substitution.Substitution;
6 6
7import java.util.Set; 7import java.util.Set;
8 8
9public enum BooleanLiteral implements PolarLiteral<BooleanLiteral> { 9public enum BooleanLiteral implements CanNegate<BooleanLiteral> {
10 TRUE(true), 10 TRUE(true),
11 FALSE(false); 11 FALSE(false);
12 12
@@ -17,8 +17,8 @@ public enum BooleanLiteral implements PolarLiteral<BooleanLiteral> {
17 } 17 }
18 18
19 @Override 19 @Override
20 public void collectAllVariables(Set<Variable> variables) { 20 public Set<Variable> getBoundVariables() {
21 // No variables to collect. 21 return Set.of();
22 } 22 }
23 23
24 @Override 24 @Override
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java
index 091b4e04..78fae7f5 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CallLiteral.java
@@ -1,109 +1,78 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.RelationLike; 3import tools.refinery.store.query.Constraint;
4import tools.refinery.store.query.Variable;
5import tools.refinery.store.query.equality.LiteralEqualityHelper; 4import tools.refinery.store.query.equality.LiteralEqualityHelper;
6import tools.refinery.store.query.substitution.Substitution; 5import tools.refinery.store.query.substitution.Substitution;
6import tools.refinery.store.query.term.NodeSort;
7import tools.refinery.store.query.term.Variable;
7 8
8import java.util.List; 9import java.util.List;
9import java.util.Objects; 10import java.util.Objects;
10import java.util.Set; 11import java.util.Set;
11 12
12public abstract class CallLiteral<T extends RelationLike> implements Literal { 13public final class CallLiteral extends AbstractCallLiteral implements CanNegate<CallLiteral> {
13 private final CallPolarity polarity; 14 private final CallPolarity polarity;
14 private final T target;
15 private final List<Variable> arguments;
16 15
17 protected CallLiteral(CallPolarity polarity, T target, List<Variable> arguments) { 16 public CallLiteral(CallPolarity polarity, Constraint target, List<Variable> arguments) {
18 if (arguments.size() != target.arity()) { 17 super(target, arguments);
19 throw new IllegalArgumentException("%s needs %d arguments, but got %s".formatted(target.name(), 18 if (polarity.isTransitive()) {
20 target.arity(), arguments.size())); 19 if (target.arity() != 2) {
21 } 20 throw new IllegalArgumentException("Transitive closures can only take binary relations");
22 if (polarity.isTransitive() && target.arity() != 2) { 21 }
23 throw new IllegalArgumentException("Transitive closures can only take binary relations"); 22 var sorts = target.getSorts();
23 if (!sorts.get(0).equals(NodeSort.INSTANCE) || !sorts.get(1).equals(NodeSort.INSTANCE)) {
24 throw new IllegalArgumentException("Transitive closures can only be computed over nodes");
25 }
24 } 26 }
25 this.polarity = polarity; 27 this.polarity = polarity;
26 this.target = target;
27 this.arguments = arguments;
28 } 28 }
29 29
30 public CallPolarity getPolarity() { 30 public CallPolarity getPolarity() {
31 return polarity; 31 return polarity;
32 } 32 }
33 33
34 public abstract Class<T> getTargetType();
35
36 public T getTarget() {
37 return target;
38 }
39
40 public List<Variable> getArguments() {
41 return arguments;
42 }
43
44 @Override 34 @Override
45 public void collectAllVariables(Set<Variable> variables) { 35 public Set<Variable> getBoundVariables() {
46 if (polarity.isPositive()) { 36 return polarity.isPositive() ? Set.copyOf(getArguments()) : Set.of();
47 variables.addAll(arguments);
48 }
49 } 37 }
50 38
51 protected List<Variable> substituteArguments(Substitution substitution) { 39 @Override
52 return arguments.stream().map(substitution::getSubstitute).toList(); 40 protected Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments) {
41 return new CallLiteral(polarity, getTarget(), substitutedArguments);
53 } 42 }
54 43
55 /** 44 @Override
56 * Compares the target of this call literal with another object. 45 public LiteralReduction getReduction() {
57 * 46 var reduction = getTarget().getReduction();
58 * @param helper Equality helper for comparing {@link Variable} and {@link tools.refinery.store.query.Dnf} 47 return polarity.isPositive() ? reduction : reduction.negate();
59 * instances.
60 * @param otherTarget The object to compare the target to.
61 * @return {@code true} if {@code otherTarget} is equal to the return value of {@link #getTarget()} according to
62 * {@code helper}, {@code false} otherwise.
63 */
64 protected boolean targetEquals(LiteralEqualityHelper helper, T otherTarget) {
65 return target.equals(otherTarget);
66 } 48 }
67 49
68 @Override 50 @Override
69 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) { 51 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
70 if (other.getClass() != getClass()) { 52 if (!super.equalsWithSubstitution(helper, other)) {
71 return false; 53 return false;
72 } 54 }
73 var otherCallLiteral = (CallLiteral<?>) other; 55 var otherCallLiteral = (CallLiteral) other;
74 if (getTargetType() != otherCallLiteral.getTargetType() || polarity != otherCallLiteral.polarity) { 56 return polarity.equals(otherCallLiteral.polarity);
75 return false; 57 }
76 } 58
77 var arity = arguments.size(); 59 @Override
78 if (arity != otherCallLiteral.arguments.size()) { 60 public CallLiteral negate() {
79 return false; 61 return new CallLiteral(polarity.negate(), getTarget(), getArguments());
80 }
81 for (int i = 0; i < arity; i++) {
82 if (!helper.variableEqual(arguments.get(i), otherCallLiteral.arguments.get(i))) {
83 return false;
84 }
85 }
86 @SuppressWarnings("unchecked")
87 var otherTarget = (T) otherCallLiteral.target;
88 return targetEquals(helper, otherTarget);
89 } 62 }
90 63
91 @Override 64 @Override
92 public boolean equals(Object o) { 65 public boolean equals(Object o) {
93 if (this == o) return true; 66 if (this == o) return true;
94 if (o == null || getClass() != o.getClass()) return false; 67 if (o == null || getClass() != o.getClass()) return false;
95 CallLiteral<?> callAtom = (CallLiteral<?>) o; 68 if (!super.equals(o)) return false;
96 return polarity == callAtom.polarity && Objects.equals(target, callAtom.target) && 69 CallLiteral that = (CallLiteral) o;
97 Objects.equals(arguments, callAtom.arguments); 70 return polarity == that.polarity;
98 } 71 }
99 72
100 @Override 73 @Override
101 public int hashCode() { 74 public int hashCode() {
102 return Objects.hash(polarity, target, arguments); 75 return Objects.hash(super.hashCode(), polarity);
103 }
104
105 protected String targetToString() {
106 return "@%s %s".formatted(getTargetType().getSimpleName(), target.name());
107 } 76 }
108 77
109 @Override 78 @Override
@@ -112,12 +81,12 @@ public abstract class CallLiteral<T extends RelationLike> implements Literal {
112 if (!polarity.isPositive()) { 81 if (!polarity.isPositive()) {
113 builder.append("!("); 82 builder.append("!(");
114 } 83 }
115 builder.append(targetToString()); 84 builder.append(getTarget().toReferenceString());
116 if (polarity.isTransitive()) { 85 if (polarity.isTransitive()) {
117 builder.append("+"); 86 builder.append("+");
118 } 87 }
119 builder.append("("); 88 builder.append("(");
120 var argumentIterator = arguments.iterator(); 89 var argumentIterator = getArguments().iterator();
121 if (argumentIterator.hasNext()) { 90 if (argumentIterator.hasNext()) {
122 builder.append(argumentIterator.next()); 91 builder.append(argumentIterator.next());
123 while (argumentIterator.hasNext()) { 92 while (argumentIterator.hasNext()) {
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CanNegate.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CanNegate.java
new file mode 100644
index 00000000..3e159c43
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CanNegate.java
@@ -0,0 +1,5 @@
1package tools.refinery.store.query.literal;
2
3public interface CanNegate<T extends CanNegate<T>> extends Literal {
4 T negate();
5}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/ConstantLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/ConstantLiteral.java
index d01c7d20..93fa3df0 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/ConstantLiteral.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/ConstantLiteral.java
@@ -1,20 +1,21 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.Variable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper; 3import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution; 4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.term.NodeVariable;
6import tools.refinery.store.query.term.Variable;
6 7
7import java.util.Set; 8import java.util.Set;
8 9
9public record ConstantLiteral(Variable variable, int nodeId) implements Literal { 10public record ConstantLiteral(NodeVariable variable, int nodeId) implements Literal {
10 @Override 11 @Override
11 public void collectAllVariables(Set<Variable> variables) { 12 public Set<Variable> getBoundVariables() {
12 variables.add(variable); 13 return Set.of(variable);
13 } 14 }
14 15
15 @Override 16 @Override
16 public ConstantLiteral substitute(Substitution substitution) { 17 public ConstantLiteral substitute(Substitution substitution) {
17 return new ConstantLiteral(substitution.getSubstitute(variable), nodeId); 18 return new ConstantLiteral(substitution.getTypeSafeSubstitute(variable), nodeId);
18 } 19 }
19 20
20 @Override 21 @Override
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CountLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CountLiteral.java
new file mode 100644
index 00000000..32e7ba3a
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/CountLiteral.java
@@ -0,0 +1,83 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.Constraint;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution;
6import tools.refinery.store.query.term.DataVariable;
7import tools.refinery.store.query.term.Variable;
8
9import java.util.List;
10import java.util.Objects;
11import java.util.Set;
12
13public class CountLiteral extends AbstractCallLiteral {
14 private final DataVariable<Integer> resultVariable;
15
16 public CountLiteral(DataVariable<Integer> resultVariable, Constraint target, List<Variable> arguments) {
17 super(target, arguments);
18 if (!resultVariable.getType().equals(Integer.class)) {
19 throw new IllegalArgumentException("Count result variable %s must be of type %s, got %s instead".formatted(
20 resultVariable, Integer.class.getName(), resultVariable.getType().getName()));
21 }
22 if (arguments.contains(resultVariable)) {
23 throw new IllegalArgumentException("Count result variable %s must not appear in the argument list"
24 .formatted(resultVariable));
25 }
26 this.resultVariable = resultVariable;
27 }
28
29 public DataVariable<Integer> getResultVariable() {
30 return resultVariable;
31 }
32
33 @Override
34 public Set<Variable> getBoundVariables() {
35 return Set.of(resultVariable);
36 }
37
38 @Override
39 protected Literal doSubstitute(Substitution substitution, List<Variable> substitutedArguments) {
40 return new CountLiteral(substitution.getTypeSafeSubstitute(resultVariable), getTarget(), substitutedArguments);
41 }
42
43 @Override
44 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
45 if (!super.equalsWithSubstitution(helper, other)) {
46 return false;
47 }
48 var otherCountLiteral = (CountLiteral) other;
49 return helper.variableEqual(resultVariable, otherCountLiteral.resultVariable);
50 }
51
52 @Override
53 public boolean equals(Object o) {
54 if (this == o) return true;
55 if (o == null || getClass() != o.getClass()) return false;
56 if (!super.equals(o)) return false;
57 CountLiteral that = (CountLiteral) o;
58 return resultVariable.equals(that.resultVariable);
59 }
60
61 @Override
62 public int hashCode() {
63 return Objects.hash(super.hashCode(), resultVariable);
64 }
65
66 @Override
67 public String toString() {
68 var builder = new StringBuilder();
69 builder.append(resultVariable);
70 builder.append(" is count ");
71 builder.append(getTarget().toReferenceString());
72 builder.append("(");
73 var argumentIterator = getArguments().iterator();
74 if (argumentIterator.hasNext()) {
75 builder.append(argumentIterator.next());
76 while (argumentIterator.hasNext()) {
77 builder.append(", ").append(argumentIterator.next());
78 }
79 }
80 builder.append(")");
81 return builder.toString();
82 }
83}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/DnfCallLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/DnfCallLiteral.java
deleted file mode 100644
index 27917265..00000000
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/DnfCallLiteral.java
+++ /dev/null
@@ -1,40 +0,0 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.Dnf;
4import tools.refinery.store.query.Variable;
5import tools.refinery.store.query.equality.LiteralEqualityHelper;
6import tools.refinery.store.query.substitution.Substitution;
7
8import java.util.List;
9
10public final class DnfCallLiteral extends CallLiteral<Dnf> implements PolarLiteral<DnfCallLiteral> {
11 public DnfCallLiteral(CallPolarity polarity, Dnf target, List<Variable> arguments) {
12 super(polarity, target, arguments);
13 }
14
15 @Override
16 public Class<Dnf> getTargetType() {
17 return Dnf.class;
18 }
19
20 @Override
21 public DnfCallLiteral substitute(Substitution substitution) {
22 return new DnfCallLiteral(getPolarity(), getTarget(), substituteArguments(substitution));
23 }
24
25 @Override
26 public DnfCallLiteral negate() {
27 return new DnfCallLiteral(getPolarity().negate(), getTarget(), getArguments());
28 }
29
30 @Override
31 public LiteralReduction getReduction() {
32 var dnfReduction = getTarget().getReduction();
33 return getPolarity().isPositive() ? dnfReduction : dnfReduction.negate();
34 }
35
36 @Override
37 protected boolean targetEquals(LiteralEqualityHelper helper, Dnf otherTarget) {
38 return helper.dnfEqual(getTarget(), otherTarget);
39 }
40}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java
index 61c753c3..4dc86b98 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/EquivalenceLiteral.java
@@ -1,17 +1,19 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.NodeVariable;
4import tools.refinery.store.query.term.Variable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper; 5import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution; 6import tools.refinery.store.query.substitution.Substitution;
6 7
7import java.util.Set; 8import java.util.Set;
8 9
9public record EquivalenceLiteral(boolean positive, Variable left, Variable right) 10public record EquivalenceLiteral(boolean positive, NodeVariable left, NodeVariable right)
10 implements PolarLiteral<EquivalenceLiteral> { 11 implements CanNegate<EquivalenceLiteral> {
11 @Override 12 @Override
12 public void collectAllVariables(Set<Variable> variables) { 13 public Set<Variable> getBoundVariables() {
13 variables.add(left); 14 // If one side of a {@code positive} equivalence is bound, it may bind its other side, but we under-approximate
14 variables.add(right); 15 // this behavior by not binding any of the sides by default.
16 return Set.of();
15 } 17 }
16 18
17 @Override 19 @Override
@@ -21,7 +23,8 @@ public record EquivalenceLiteral(boolean positive, Variable left, Variable right
21 23
22 @Override 24 @Override
23 public EquivalenceLiteral substitute(Substitution substitution) { 25 public EquivalenceLiteral substitute(Substitution substitution) {
24 return new EquivalenceLiteral(positive, substitution.getSubstitute(left), substitution.getSubstitute(right)); 26 return new EquivalenceLiteral(positive, substitution.getTypeSafeSubstitute(left),
27 substitution.getTypeSafeSubstitute(right));
25 } 28 }
26 29
27 @Override 30 @Override
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literal.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literal.java
index ddd91775..6347410e 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literal.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literal.java
@@ -1,13 +1,13 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.Variable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper; 4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.substitution.Substitution; 5import tools.refinery.store.query.substitution.Substitution;
6 6
7import java.util.Set; 7import java.util.Set;
8 8
9public interface Literal { 9public interface Literal {
10 void collectAllVariables(Set<Variable> variables); 10 Set<Variable> getBoundVariables();
11 11
12 Literal substitute(Substitution substitution); 12 Literal substitute(Substitution substitution);
13 13
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literals.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literals.java
index 2c7e893f..89039352 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literals.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/Literals.java
@@ -1,11 +1,17 @@
1package tools.refinery.store.query.literal; 1package tools.refinery.store.query.literal;
2 2
3import tools.refinery.store.query.term.Term;
4
3public final class Literals { 5public final class Literals {
4 private Literals() { 6 private Literals() {
5 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 7 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
6 } 8 }
7 9
8 public static <T extends PolarLiteral<T>> T not(PolarLiteral<T> literal) { 10 public static <T extends CanNegate<T>> T not(CanNegate<T> literal) {
9 return literal.negate(); 11 return literal.negate();
10 } 12 }
13
14 public static AssumeLiteral assume(Term<Boolean> term) {
15 return new AssumeLiteral(term);
16 }
11} 17}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/PolarLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/PolarLiteral.java
deleted file mode 100644
index 32523675..00000000
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/PolarLiteral.java
+++ /dev/null
@@ -1,5 +0,0 @@
1package tools.refinery.store.query.literal;
2
3public interface PolarLiteral<T extends PolarLiteral<T>> extends Literal {
4 T negate();
5}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/RelationViewLiteral.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/RelationViewLiteral.java
deleted file mode 100644
index fb8b3332..00000000
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/literal/RelationViewLiteral.java
+++ /dev/null
@@ -1,35 +0,0 @@
1package tools.refinery.store.query.literal;
2
3import tools.refinery.store.query.Variable;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.view.AnyRelationView;
6
7import java.util.List;
8
9public final class RelationViewLiteral extends CallLiteral<AnyRelationView>
10 implements PolarLiteral<RelationViewLiteral> {
11 public RelationViewLiteral(CallPolarity polarity, AnyRelationView target, List<Variable> arguments) {
12 super(polarity, target, arguments);
13 }
14
15 @Override
16 public Class<AnyRelationView> getTargetType() {
17 return AnyRelationView.class;
18 }
19
20 @Override
21 protected String targetToString() {
22 var target = getTarget();
23 return "@RelationView(\"%s\") %s".formatted(target.getViewName(), target.getSymbol().name());
24 }
25
26 @Override
27 public RelationViewLiteral substitute(Substitution substitution) {
28 return new RelationViewLiteral(getPolarity(), getTarget(), substituteArguments(substitution));
29 }
30
31 @Override
32 public RelationViewLiteral negate() {
33 return new RelationViewLiteral(getPolarity().negate(), getTarget(), getArguments());
34 }
35}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/CompositeSubstitution.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/CompositeSubstitution.java
new file mode 100644
index 00000000..f8064ca2
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/CompositeSubstitution.java
@@ -0,0 +1,10 @@
1package tools.refinery.store.query.substitution;
2
3import tools.refinery.store.query.term.Variable;
4
5public record CompositeSubstitution(Substitution first, Substitution second) implements Substitution {
6 @Override
7 public Variable getSubstitute(Variable variable) {
8 return second.getSubstitute(first.getSubstitute(variable));
9 }
10}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/MapBasedSubstitution.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/MapBasedSubstitution.java
index ffc65047..c7754619 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/MapBasedSubstitution.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/MapBasedSubstitution.java
@@ -1,6 +1,6 @@
1package tools.refinery.store.query.substitution; 1package tools.refinery.store.query.substitution;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.Variable;
4 4
5import java.util.Map; 5import java.util.Map;
6 6
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/RenewingSubstitution.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/RenewingSubstitution.java
index 54d18a3f..7847e582 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/RenewingSubstitution.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/RenewingSubstitution.java
@@ -1,6 +1,6 @@
1package tools.refinery.store.query.substitution; 1package tools.refinery.store.query.substitution;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.Variable;
4 4
5import java.util.HashMap; 5import java.util.HashMap;
6import java.util.Map; 6import java.util.Map;
@@ -10,10 +10,6 @@ public class RenewingSubstitution implements Substitution {
10 10
11 @Override 11 @Override
12 public Variable getSubstitute(Variable variable) { 12 public Variable getSubstitute(Variable variable) {
13 return alreadyRenewed.computeIfAbsent(variable, RenewingSubstitution::renew); 13 return alreadyRenewed.computeIfAbsent(variable, Variable::renew);
14 }
15
16 private static Variable renew(Variable variable) {
17 return variable.isExplicitlyNamed() ? new Variable(variable.getName()) : new Variable();
18 } 14 }
19} 15}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/StatelessSubstitution.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/StatelessSubstitution.java
index d33ad6fb..eed414d9 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/StatelessSubstitution.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/StatelessSubstitution.java
@@ -1,6 +1,6 @@
1package tools.refinery.store.query.substitution; 1package tools.refinery.store.query.substitution;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.Variable;
4 4
5public enum StatelessSubstitution implements Substitution { 5public enum StatelessSubstitution implements Substitution {
6 FAILING { 6 FAILING {
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitution.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitution.java
index 9d086bf5..99f84b9e 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitution.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitution.java
@@ -1,8 +1,29 @@
1package tools.refinery.store.query.substitution; 1package tools.refinery.store.query.substitution;
2 2
3import tools.refinery.store.query.Variable; 3import tools.refinery.store.query.term.AnyDataVariable;
4import tools.refinery.store.query.term.DataVariable;
5import tools.refinery.store.query.term.NodeVariable;
6import tools.refinery.store.query.term.Variable;
4 7
5@FunctionalInterface 8@FunctionalInterface
6public interface Substitution { 9public interface Substitution {
7 Variable getSubstitute(Variable variable); 10 Variable getSubstitute(Variable variable);
11
12 default NodeVariable getTypeSafeSubstitute(NodeVariable variable) {
13 var substitute = getSubstitute(variable);
14 return substitute.asNodeVariable();
15 }
16
17 default AnyDataVariable getTypeSafeSubstitute(AnyDataVariable variable) {
18 return getTypeSafeSubstitute((DataVariable<?>) variable);
19 }
20
21 default <T> DataVariable<T> getTypeSafeSubstitute(DataVariable<T> variable) {
22 var substitute = getSubstitute(variable);
23 return substitute.asDataVariable(variable.getType());
24 }
25
26 default Substitution andThen(Substitution second) {
27 return new CompositeSubstitution(this, second);
28 }
8} 29}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitutions.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitutions.java
index 26cf1a20..5d4654da 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitutions.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/substitution/Substitutions.java
@@ -1,6 +1,8 @@
1package tools.refinery.store.query.substitution; 1package tools.refinery.store.query.substitution;
2 2
3import tools.refinery.store.query.Variable; 3import org.jetbrains.annotations.NotNull;
4import org.jetbrains.annotations.Nullable;
5import tools.refinery.store.query.term.Variable;
4 6
5import java.util.Map; 7import java.util.Map;
6 8
@@ -24,4 +26,8 @@ public final class Substitutions {
24 public static Substitution renewing() { 26 public static Substitution renewing() {
25 return new RenewingSubstitution(); 27 return new RenewingSubstitution();
26 } 28 }
29
30 public static Substitution compose(@Nullable Substitution first, @NotNull Substitution second) {
31 return first == null ? second : first.andThen(second);
32 }
27} 33}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Aggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Aggregator.java
new file mode 100644
index 00000000..47421a94
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Aggregator.java
@@ -0,0 +1,13 @@
1package tools.refinery.store.query.term;
2
3import java.util.stream.Stream;
4
5public interface Aggregator<R, T> {
6 Class<R> getResultType();
7
8 Class<T> getInputType();
9
10 R aggregateStream(Stream<T> stream);
11
12 R getEmptyResult();
13}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java
new file mode 100644
index 00000000..ecfefcf9
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyDataVariable.java
@@ -0,0 +1,33 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5
6import java.util.Set;
7
8public abstract sealed class AnyDataVariable extends Variable implements AnyTerm permits DataVariable {
9 protected AnyDataVariable(String name) {
10 super(name);
11 }
12
13 @Override
14 public NodeVariable asNodeVariable() {
15 throw new IllegalStateException("%s is a data variable".formatted(this));
16 }
17
18 @Override
19 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
20 return other instanceof AnyDataVariable dataVariable && helper.variableEqual(this, dataVariable);
21 }
22
23 @Override
24 public Set<AnyDataVariable> getInputVariables() {
25 return Set.of(this);
26 }
27
28 @Override
29 public abstract AnyDataVariable renew(@Nullable String name);
30
31 @Override
32 public abstract AnyDataVariable renew();
33}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyTerm.java
new file mode 100644
index 00000000..8f998d45
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AnyTerm.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5
6import java.util.Set;
7
8public sealed interface AnyTerm permits AnyDataVariable, Term {
9 Class<?> getType();
10
11 AnyTerm substitute(Substitution substitution);
12
13 boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other);
14
15 Set<AnyDataVariable> getInputVariables();
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryOperator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryOperator.java
new file mode 100644
index 00000000..8706a046
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryOperator.java
@@ -0,0 +1,26 @@
1package tools.refinery.store.query.term;
2
3public enum ArithmeticBinaryOperator {
4 ADD("+", true),
5 SUB("-", true),
6 MUL("*", true),
7 DIV("/", true),
8 POW("**", true),
9 MIN("min", false),
10 MAX("max", false);
11
12 private final String text;
13 private final boolean infix;
14
15 ArithmeticBinaryOperator(String text, boolean infix) {
16 this.text = text;
17 this.infix = infix;
18 }
19
20 public String formatString(String left, String right) {
21 if (infix) {
22 return "(%s) %s (%s)".formatted(left, text, right);
23 }
24 return "%s(%s, %s)".formatted(text, left, right);
25 }
26}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryTerm.java
new file mode 100644
index 00000000..887a1e6e
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticBinaryTerm.java
@@ -0,0 +1,56 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4
5import java.util.Objects;
6
7public abstract class ArithmeticBinaryTerm<T> extends BinaryTerm<T, T, T> {
8 private final ArithmeticBinaryOperator operator;
9
10 protected ArithmeticBinaryTerm(ArithmeticBinaryOperator operator, Term<T> left, Term<T> right) {
11 super(left, right);
12 this.operator = operator;
13 }
14
15 @Override
16 public Class<T> getLeftType() {
17 return getType();
18 }
19
20 @Override
21 public Class<T> getRightType() {
22 return getType();
23 }
24
25 public ArithmeticBinaryOperator getOperator() {
26 return operator;
27 }
28
29 @Override
30 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
31 if (!super.equalsWithSubstitution(helper, other)) {
32 return false;
33 }
34 var otherArithmeticBinaryTerm = (ArithmeticBinaryTerm<?>) other;
35 return operator == otherArithmeticBinaryTerm.operator;
36 }
37
38 @Override
39 public String toString() {
40 return operator.formatString(getLeft().toString(), getRight().toString());
41 }
42
43 @Override
44 public boolean equals(Object o) {
45 if (this == o) return true;
46 if (o == null || getClass() != o.getClass()) return false;
47 if (!super.equals(o)) return false;
48 ArithmeticBinaryTerm<?> that = (ArithmeticBinaryTerm<?>) o;
49 return operator == that.operator;
50 }
51
52 @Override
53 public int hashCode() {
54 return Objects.hash(super.hashCode(), operator);
55 }
56}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryOperator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryOperator.java
new file mode 100644
index 00000000..6a7c25db
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryOperator.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.term;
2
3public enum ArithmeticUnaryOperator {
4 PLUS("+"),
5 MINUS("-");
6
7 private final String prefix;
8
9 ArithmeticUnaryOperator(String prefix) {
10 this.prefix = prefix;
11 }
12
13 public String formatString(String body) {
14 return "%s(%s)".formatted(prefix, body);
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryTerm.java
new file mode 100644
index 00000000..b78239c7
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ArithmeticUnaryTerm.java
@@ -0,0 +1,51 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4
5import java.util.Objects;
6
7public abstract class ArithmeticUnaryTerm<T> extends UnaryTerm<T, T> {
8 private final ArithmeticUnaryOperator operator;
9
10 protected ArithmeticUnaryTerm(ArithmeticUnaryOperator operator, Term<T> body) {
11 super(body);
12 this.operator = operator;
13 }
14
15 @Override
16 public Class<T> getBodyType() {
17 return getType();
18 }
19
20 public ArithmeticUnaryOperator getOperator() {
21 return operator;
22 }
23
24 @Override
25 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
26 if (!super.equalsWithSubstitution(helper, other)) {
27 return false;
28 }
29 var otherArithmeticUnaryTerm = (ArithmeticUnaryTerm<?>) other;
30 return operator == otherArithmeticUnaryTerm.operator;
31 }
32
33 @Override
34 public String toString() {
35 return operator.formatString(getBody().toString());
36 }
37
38 @Override
39 public boolean equals(Object o) {
40 if (this == o) return true;
41 if (o == null || getClass() != o.getClass()) return false;
42 if (!super.equals(o)) return false;
43 ArithmeticUnaryTerm<?> that = (ArithmeticUnaryTerm<?>) o;
44 return operator == that.operator;
45 }
46
47 @Override
48 public int hashCode() {
49 return Objects.hash(super.hashCode(), operator);
50 }
51}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AssignedValue.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AssignedValue.java
new file mode 100644
index 00000000..465e690f
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/AssignedValue.java
@@ -0,0 +1,8 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.literal.Literal;
4
5@FunctionalInterface
6public interface AssignedValue<T> {
7 Literal toLiteral(DataVariable<T> targetVariable);
8}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/BinaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/BinaryTerm.java
new file mode 100644
index 00000000..34f48ccc
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/BinaryTerm.java
@@ -0,0 +1,93 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.valuation.Valuation;
6
7import java.util.Collections;
8import java.util.HashSet;
9import java.util.Objects;
10import java.util.Set;
11
12public abstract class BinaryTerm<R, T1, T2> implements Term<R> {
13 private final Term<T1> left;
14 private final Term<T2> right;
15
16 protected BinaryTerm(Term<T1> left, Term<T2> right) {
17 if (!left.getType().equals(getLeftType())) {
18 throw new IllegalArgumentException("Expected left %s to be of type %s, got %s instead".formatted(left,
19 getLeftType().getName(), left.getType().getName()));
20 }
21 if (!right.getType().equals(getRightType())) {
22 throw new IllegalArgumentException("Expected right %s to be of type %s, got %s instead".formatted(right,
23 getRightType().getName(), right.getType().getName()));
24 }
25 this.left = left;
26 this.right = right;
27 }
28
29 public abstract Class<T1> getLeftType();
30
31 public abstract Class<T2> getRightType();
32
33 public Term<T1> getLeft() {
34 return left;
35 }
36
37 public Term<T2> getRight() {
38 return right;
39 }
40
41 @Override
42 public R evaluate(Valuation valuation) {
43 var leftValue = left.evaluate(valuation);
44 if (leftValue == null) {
45 return null;
46 }
47 var rightValue = right.evaluate(valuation);
48 if (rightValue == null) {
49 return null;
50 }
51 return doEvaluate(leftValue, rightValue);
52 }
53
54 protected abstract R doEvaluate(T1 leftValue, T2 rightValue);
55
56 @Override
57 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
58 if (getClass() != other.getClass()) {
59 return false;
60 }
61 var otherBinaryTerm = (BinaryTerm<?, ?, ?>) other;
62 return left.equalsWithSubstitution(helper, otherBinaryTerm.left) && right.equalsWithSubstitution(helper,
63 otherBinaryTerm.right);
64 }
65
66 @Override
67 public Term<R> substitute(Substitution substitution) {
68 return doSubstitute(substitution, left.substitute(substitution), right.substitute(substitution));
69 }
70
71 public abstract Term<R> doSubstitute(Substitution substitution, Term<T1> substitutedLeft,
72 Term<T2> substitutedRight);
73
74 @Override
75 public Set<AnyDataVariable> getInputVariables() {
76 var inputVariables = new HashSet<>(left.getInputVariables());
77 inputVariables.addAll(right.getInputVariables());
78 return Collections.unmodifiableSet(inputVariables);
79 }
80
81 @Override
82 public boolean equals(Object o) {
83 if (this == o) return true;
84 if (o == null || getClass() != o.getClass()) return false;
85 BinaryTerm<?, ?, ?> that = (BinaryTerm<?, ?, ?>) o;
86 return left.equals(that.left) && right.equals(that.right);
87 }
88
89 @Override
90 public int hashCode() {
91 return Objects.hash(getClass(), left, right);
92 }
93}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonOperator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonOperator.java
new file mode 100644
index 00000000..44dcce10
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonOperator.java
@@ -0,0 +1,20 @@
1package tools.refinery.store.query.term;
2
3public enum ComparisonOperator {
4 EQ("=="),
5 NOT_EQ("!="),
6 LESS("<"),
7 LESS_EQ("<="),
8 GREATER(">"),
9 GREATER_EQ(">=");
10
11 private final String text;
12
13 ComparisonOperator(String text) {
14 this.text = text;
15 }
16
17 public String formatString(String left, String right) {
18 return "(%s) %s (%s)".formatted(left, text, right);
19 }
20}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonTerm.java
new file mode 100644
index 00000000..320d42df
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ComparisonTerm.java
@@ -0,0 +1,63 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4
5import java.util.Objects;
6
7public abstract class ComparisonTerm<T> extends BinaryTerm<Boolean, T, T> {
8 private final ComparisonOperator operator;
9
10 protected ComparisonTerm(ComparisonOperator operator, Term<T> left, Term<T> right) {
11 super(left, right);
12 this.operator = operator;
13 }
14
15 @Override
16 public Class<Boolean> getType() {
17 return Boolean.class;
18 }
19
20 public abstract Class<T> getOperandType();
21
22 @Override
23 public Class<T> getLeftType() {
24 return getOperandType();
25 }
26
27 @Override
28 public Class<T> getRightType() {
29 return getOperandType();
30 }
31
32 public ComparisonOperator getOperator() {
33 return operator;
34 }
35
36 @Override
37 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
38 if (!super.equalsWithSubstitution(helper, other)) {
39 return false;
40 }
41 var otherComparisonTerm = (ComparisonTerm<?>) other;
42 return operator == otherComparisonTerm.operator;
43 }
44
45 @Override
46 public String toString() {
47 return operator.formatString(getLeft().toString(), getRight().toString());
48 }
49
50 @Override
51 public boolean equals(Object o) {
52 if (this == o) return true;
53 if (o == null || getClass() != o.getClass()) return false;
54 if (!super.equals(o)) return false;
55 ComparisonTerm<?> that = (ComparisonTerm<?>) o;
56 return operator == that.operator;
57 }
58
59 @Override
60 public int hashCode() {
61 return Objects.hash(super.hashCode(), operator);
62 }
63}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ConstantTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ConstantTerm.java
new file mode 100644
index 00000000..2185fe37
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ConstantTerm.java
@@ -0,0 +1,52 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.valuation.Valuation;
6
7import java.util.Set;
8
9public record ConstantTerm<T>(Class<T> type, T value) implements Term<T> {
10 public ConstantTerm {
11 if (value == null) {
12 throw new IllegalArgumentException("value should not be null");
13 }
14 if (!type.isInstance(value)) {
15 throw new IllegalArgumentException("value %s is not an instance of %s".formatted(value, type.getName()));
16 }
17 }
18
19 @Override
20 public Class<T> getType() {
21 return type;
22 }
23
24 public T getValue() {
25 return value;
26 }
27
28 @Override
29 public T evaluate(Valuation valuation) {
30 return getValue();
31 }
32
33 @Override
34 public Term<T> substitute(Substitution substitution) {
35 return this;
36 }
37
38 @Override
39 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
40 return equals(other);
41 }
42
43 @Override
44 public Set<AnyDataVariable> getInputVariables() {
45 return Set.of();
46 }
47
48 @Override
49 public String toString() {
50 return getValue().toString();
51 }
52}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataSort.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataSort.java
new file mode 100644
index 00000000..4fb44492
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataSort.java
@@ -0,0 +1,29 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4
5public record DataSort<T>(Class<T> type) implements Sort {
6 public static final DataSort<Integer> INT = new DataSort<>(Integer.class);
7
8 public static final DataSort<Boolean> BOOL = new DataSort<>(Boolean.class);
9
10 @Override
11 public boolean isInstance(Variable variable) {
12 return variable instanceof DataVariable<?> dataVariable && type.equals(dataVariable.getType());
13 }
14
15 @Override
16 public DataVariable<T> newInstance(@Nullable String name) {
17 return Variable.of(name, type);
18 }
19
20 @Override
21 public DataVariable<T> newInstance() {
22 return newInstance(null);
23 }
24
25 @Override
26 public String toString() {
27 return type.getName();
28 }
29}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java
new file mode 100644
index 00000000..af070ca7
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/DataVariable.java
@@ -0,0 +1,87 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.literal.Literal;
6import tools.refinery.store.query.substitution.Substitution;
7import tools.refinery.store.query.valuation.Valuation;
8
9import java.util.Objects;
10
11public final class DataVariable<T> extends AnyDataVariable implements Term<T> {
12 private final Class<T> type;
13
14 DataVariable(String name, Class<T> type) {
15 super(name);
16 this.type = type;
17 }
18
19 @Override
20 public DataSort<T> getSort() {
21 return new DataSort<>(getType());
22 }
23
24 @Override
25 public Class<T> getType() {
26 return type;
27 }
28
29 @Override
30 public DataVariable<T> renew(@Nullable String name) {
31 return new DataVariable<>(name, type);
32 }
33
34 @Override
35 public DataVariable<T> renew() {
36 return renew(getExplicitName());
37 }
38
39 @Override
40 public NodeVariable asNodeVariable() {
41 throw new IllegalStateException("%s is a data variable".formatted(this));
42 }
43
44 @Override
45 public <U> DataVariable<U> asDataVariable(Class<U> newType) {
46 if (!getType().equals(newType)) {
47 throw new IllegalStateException("%s is not of type %s but of type %s".formatted(this, newType.getName(),
48 getType().getName()));
49 }
50 @SuppressWarnings("unchecked")
51 var result = (DataVariable<U>) this;
52 return result;
53 }
54
55 @Override
56 public T evaluate(Valuation valuation) {
57 return valuation.getValue(this);
58 }
59
60 @Override
61 public Term<T> substitute(Substitution substitution) {
62 return substitution.getTypeSafeSubstitute(this);
63 }
64
65 @Override
66 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
67 return other instanceof DataVariable<?> dataVariable && helper.variableEqual(this, dataVariable);
68 }
69
70 public Literal assign(AssignedValue<T> value) {
71 return value.toLiteral(this);
72 }
73
74 @Override
75 public boolean equals(Object o) {
76 if (this == o) return true;
77 if (o == null || getClass() != o.getClass()) return false;
78 if (!super.equals(o)) return false;
79 DataVariable<?> that = (DataVariable<?>) o;
80 return type.equals(that.type);
81 }
82
83 @Override
84 public int hashCode() {
85 return Objects.hash(super.hashCode(), type);
86 }
87}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ExtremeValueAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ExtremeValueAggregator.java
new file mode 100644
index 00000000..57ff597c
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/ExtremeValueAggregator.java
@@ -0,0 +1,103 @@
1package tools.refinery.store.query.term;
2
3import java.util.Comparator;
4import java.util.Objects;
5import java.util.SortedMap;
6import java.util.TreeMap;
7
8public class ExtremeValueAggregator<T> implements StatefulAggregator<T, T> {
9 private final Class<T> type;
10 private final T emptyResult;
11 private final Comparator<T> comparator;
12
13 public ExtremeValueAggregator(Class<T> type, T emptyResult) {
14 this(type, emptyResult, null);
15 }
16
17 public ExtremeValueAggregator(Class<T> type, T emptyResult, Comparator<T> comparator) {
18 this.type = type;
19 this.emptyResult = emptyResult;
20 this.comparator = comparator;
21 }
22
23 @Override
24 public Class<T> getResultType() {
25 return getInputType();
26 }
27
28 @Override
29 public Class<T> getInputType() {
30 return type;
31 }
32
33 @Override
34 public StatefulAggregate<T, T> createEmptyAggregate() {
35 return new Aggregate();
36 }
37
38 @Override
39 public T getEmptyResult() {
40 return emptyResult;
41 }
42
43 @Override
44 public boolean equals(Object o) {
45 if (this == o) return true;
46 if (o == null || getClass() != o.getClass()) return false;
47 ExtremeValueAggregator<?> that = (ExtremeValueAggregator<?>) o;
48 return type.equals(that.type) && Objects.equals(emptyResult, that.emptyResult) && Objects.equals(comparator,
49 that.comparator);
50 }
51
52 @Override
53 public int hashCode() {
54 return Objects.hash(type, emptyResult, comparator);
55 }
56
57 private class Aggregate implements StatefulAggregate<T, T> {
58 private final SortedMap<T, Integer> values;
59
60 private Aggregate() {
61 values = new TreeMap<>(comparator);
62 }
63
64 private Aggregate(Aggregate other) {
65 values = new TreeMap<>(other.values);
66 }
67
68 @Override
69 public void add(T value) {
70 values.compute(value, (ignoredValue, currentCount) -> currentCount == null ? 1 : currentCount + 1);
71 }
72
73 @Override
74 public void remove(T value) {
75 values.compute(value, (theValue, currentCount) -> {
76 if (currentCount == null || currentCount <= 0) {
77 throw new IllegalStateException("Invalid count %d for value %s".formatted(currentCount, theValue));
78 }
79 return currentCount.equals(1) ? null : currentCount - 1;
80 });
81 }
82
83 @Override
84 public T getResult() {
85 return isEmpty() ? emptyResult : values.firstKey();
86 }
87
88 @Override
89 public boolean isEmpty() {
90 return values.isEmpty();
91 }
92
93 @Override
94 public StatefulAggregate<T, T> deepCopy() {
95 return new Aggregate(this);
96 }
97
98 @Override
99 public boolean contains(T value) {
100 return StatefulAggregate.super.contains(value);
101 }
102 }
103}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeSort.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeSort.java
new file mode 100644
index 00000000..1a4b2d4b
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeSort.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4
5public final class NodeSort implements Sort {
6 public static final NodeSort INSTANCE = new NodeSort();
7
8 private NodeSort() {
9 }
10
11 @Override
12 public boolean isInstance(Variable variable) {
13 return variable instanceof NodeVariable;
14 }
15
16 @Override
17 public NodeVariable newInstance(@Nullable String name) {
18 return new NodeVariable(name);
19 }
20
21 @Override
22 public NodeVariable newInstance() {
23 return newInstance(null);
24 }
25
26 @Override
27 public String toString() {
28 return "<node>";
29 }
30}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java
new file mode 100644
index 00000000..7419aaad
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/NodeVariable.java
@@ -0,0 +1,48 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.query.literal.ConstantLiteral;
5import tools.refinery.store.query.literal.EquivalenceLiteral;
6
7public final class NodeVariable extends Variable {
8 NodeVariable(@Nullable String name) {
9 super(name);
10 }
11
12 @Override
13 public NodeSort getSort() {
14 return NodeSort.INSTANCE;
15 }
16
17 @Override
18 public NodeVariable renew(@Nullable String name) {
19 return Variable.of(name);
20 }
21
22 @Override
23 public NodeVariable renew() {
24 return renew(getExplicitName());
25 }
26
27 @Override
28 public NodeVariable asNodeVariable() {
29 return this;
30 }
31
32 @Override
33 public <T> DataVariable<T> asDataVariable(Class<T> type) {
34 throw new IllegalStateException("%s is a node variable".formatted(this));
35 }
36
37 public ConstantLiteral isConstant(int value) {
38 return new ConstantLiteral(this, value);
39 }
40
41 public EquivalenceLiteral isEquivalent(NodeVariable other) {
42 return new EquivalenceLiteral(true, this, other);
43 }
44
45 public EquivalenceLiteral notEquivalent(NodeVariable other) {
46 return new EquivalenceLiteral(false, this, other);
47 }
48}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/OpaqueTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/OpaqueTerm.java
new file mode 100644
index 00000000..8faa9c75
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/OpaqueTerm.java
@@ -0,0 +1,80 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.substitution.Substitutions;
6import tools.refinery.store.query.valuation.Valuation;
7
8import java.util.Objects;
9import java.util.Set;
10import java.util.function.Function;
11import java.util.stream.Collectors;
12
13public final class OpaqueTerm<T> implements Term<T> {
14 private final Class<T> type;
15 private final Function<? super Valuation, ? extends T> evaluator;
16 private final Set<AnyDataVariable> variables;
17 private final Substitution substitution;
18
19 public OpaqueTerm(Class<T> type, Function<? super Valuation, ? extends T> evaluator,
20 Set<? extends AnyDataVariable> variables) {
21 this(type, evaluator, variables, null);
22 }
23
24 private OpaqueTerm(Class<T> type, Function<? super Valuation, ? extends T> evaluator,
25 Set<? extends AnyDataVariable> variables, Substitution substitution) {
26 this.type = type;
27 this.evaluator = evaluator;
28 this.variables = Set.copyOf(variables);
29 this.substitution = substitution;
30 }
31
32 @Override
33 public Class<T> getType() {
34 return type;
35 }
36
37 @Override
38 public Set<AnyDataVariable> getInputVariables() {
39 return variables;
40 }
41
42 @Override
43 public T evaluate(Valuation valuation) {
44 return evaluator.apply(valuation.substitute(substitution));
45 }
46
47 @Override
48 public Term<T> substitute(Substitution newSubstitution) {
49 var substitutedVariables = variables.stream()
50 .map(newSubstitution::getTypeSafeSubstitute)
51 .collect(Collectors.toUnmodifiableSet());
52 return new OpaqueTerm<>(type, evaluator, substitutedVariables,
53 Substitutions.compose(substitution, newSubstitution));
54 }
55
56 @Override
57 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
58 // Cannot inspect the opaque evaluator for deep equality.
59 return equals(other);
60 }
61
62 @Override
63 public String toString() {
64 return "<opaque>";
65 }
66
67 @Override
68 public boolean equals(Object o) {
69 if (this == o) return true;
70 if (o == null || getClass() != o.getClass()) return false;
71 OpaqueTerm<?> that = (OpaqueTerm<?>) o;
72 return type.equals(that.type) && evaluator.equals(that.evaluator) && Objects.equals(substitution,
73 that.substitution);
74 }
75
76 @Override
77 public int hashCode() {
78 return Objects.hash(type, evaluator, substitution);
79 }
80}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Sort.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Sort.java
new file mode 100644
index 00000000..622bcfce
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Sort.java
@@ -0,0 +1,11 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4
5public sealed interface Sort permits DataSort, NodeSort {
6 boolean isInstance(Variable variable);
7
8 Variable newInstance(@Nullable String name);
9
10 Variable newInstance();
11}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregate.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregate.java
new file mode 100644
index 00000000..7ce91305
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregate.java
@@ -0,0 +1,17 @@
1package tools.refinery.store.query.term;
2
3public interface StatefulAggregate<R, T> {
4 void add(T value);
5
6 void remove(T value);
7
8 R getResult();
9
10 boolean isEmpty();
11
12 StatefulAggregate<R, T> deepCopy();
13
14 default boolean contains(T value) {
15 throw new UnsupportedOperationException();
16 }
17}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregator.java
new file mode 100644
index 00000000..c215a511
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatefulAggregator.java
@@ -0,0 +1,23 @@
1package tools.refinery.store.query.term;
2
3import java.util.stream.Stream;
4
5public interface StatefulAggregator<R, T> extends Aggregator<R, T> {
6 StatefulAggregate<R, T> createEmptyAggregate();
7
8 @Override
9 default R aggregateStream(Stream<T> stream) {
10 var accumulator = createEmptyAggregate();
11 var iterator = stream.iterator();
12 while (iterator.hasNext()) {
13 var value = iterator.next();
14 accumulator.add(value);
15 }
16 return accumulator.getResult();
17 }
18
19 @Override
20 default R getEmptyResult() {
21 return createEmptyAggregate().getResult();
22 }
23}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatelessAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatelessAggregator.java
new file mode 100644
index 00000000..74dbd335
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/StatelessAggregator.java
@@ -0,0 +1,20 @@
1package tools.refinery.store.query.term;
2
3import java.util.stream.Stream;
4
5public interface StatelessAggregator<R, T> extends Aggregator<R, T> {
6 R add(R current, T value);
7
8 R remove(R current, T value);
9
10 @Override
11 default R aggregateStream(Stream<T> stream) {
12 var accumulator = getEmptyResult();
13 var iterator = stream.iterator();
14 while (iterator.hasNext()) {
15 var value = iterator.next();
16 accumulator = add(accumulator, value);
17 }
18 return accumulator;
19 }
20}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Term.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Term.java
new file mode 100644
index 00000000..95434db2
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Term.java
@@ -0,0 +1,21 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.literal.AssignLiteral;
4import tools.refinery.store.query.literal.Literal;
5import tools.refinery.store.query.substitution.Substitution;
6import tools.refinery.store.query.valuation.Valuation;
7
8public non-sealed interface Term<T> extends AnyTerm, AssignedValue<T> {
9 @Override
10 Class<T> getType();
11
12 T evaluate(Valuation valuation);
13
14 @Override
15 Term<T> substitute(Substitution substitution);
16
17 @Override
18 default Literal toLiteral(DataVariable<T> targetVariable) {
19 return new AssignLiteral<>(targetVariable, this);
20 }
21}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/UnaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/UnaryTerm.java
new file mode 100644
index 00000000..4083111a
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/UnaryTerm.java
@@ -0,0 +1,68 @@
1package tools.refinery.store.query.term;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.valuation.Valuation;
6
7import java.util.Objects;
8import java.util.Set;
9
10public abstract class UnaryTerm<R, T> implements Term<R> {
11 private final Term<T> body;
12
13 protected UnaryTerm(Term<T> body) {
14 if (!body.getType().equals(getBodyType())) {
15 throw new IllegalArgumentException("Expected body %s to be of type %s, got %s instead".formatted(body,
16 getBodyType().getName(), body.getType().getName()));
17 }
18 this.body = body;
19 }
20
21 public abstract Class<T> getBodyType();
22
23 public Term<T> getBody() {
24 return body;
25 }
26
27 @Override
28 public R evaluate(Valuation valuation) {
29 var bodyValue = body.evaluate(valuation);
30 return bodyValue == null ? null : doEvaluate(bodyValue);
31 }
32
33 protected abstract R doEvaluate(T bodyValue);
34
35 @Override
36 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
37 if (getClass() != other.getClass()) {
38 return false;
39 }
40 var otherUnaryTerm = (UnaryTerm<?, ?>) other;
41 return body.equalsWithSubstitution(helper, otherUnaryTerm.body);
42 }
43
44 @Override
45 public Term<R> substitute(Substitution substitution) {
46 return doSubstitute(substitution, body.substitute(substitution));
47 }
48
49 protected abstract Term<R> doSubstitute(Substitution substitution, Term<T> substitutedBody);
50
51 @Override
52 public Set<AnyDataVariable> getInputVariables() {
53 return body.getInputVariables();
54 }
55
56 @Override
57 public boolean equals(Object o) {
58 if (this == o) return true;
59 if (o == null || getClass() != o.getClass()) return false;
60 UnaryTerm<?, ?> unaryTerm = (UnaryTerm<?, ?>) o;
61 return body.equals(unaryTerm.body);
62 }
63
64 @Override
65 public int hashCode() {
66 return Objects.hash(getClass(), body);
67 }
68}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java
new file mode 100644
index 00000000..957e10f8
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/Variable.java
@@ -0,0 +1,76 @@
1package tools.refinery.store.query.term;
2
3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.query.dnf.DnfUtils;
5
6import java.util.Objects;
7
8public abstract sealed class Variable permits AnyDataVariable, NodeVariable {
9 private final String explicitName;
10 private final String uniqueName;
11
12 protected Variable(String name) {
13 this.explicitName = name;
14 uniqueName = DnfUtils.generateUniqueName(name);
15 }
16
17 public abstract Sort getSort();
18
19 public String getName() {
20 return explicitName == null ? uniqueName : explicitName;
21 }
22
23 protected String getExplicitName() {
24 return explicitName;
25 }
26
27 public boolean isExplicitlyNamed() {
28 return explicitName != null;
29 }
30
31 public String getUniqueName() {
32 return uniqueName;
33 }
34
35 public abstract Variable renew(@Nullable String name);
36
37 public abstract Variable renew();
38
39 public abstract NodeVariable asNodeVariable();
40
41 public abstract <T> DataVariable<T> asDataVariable(Class<T> type);
42
43 @Override
44 public String toString() {
45 return getName();
46 }
47
48 @Override
49 public boolean equals(Object o) {
50 if (this == o) return true;
51 if (o == null || getClass() != o.getClass()) return false;
52 Variable variable = (Variable) o;
53 return Objects.equals(uniqueName, variable.uniqueName);
54 }
55
56 @Override
57 public int hashCode() {
58 return Objects.hash(uniqueName);
59 }
60
61 public static NodeVariable of(@Nullable String name) {
62 return new NodeVariable(name);
63 }
64
65 public static NodeVariable of() {
66 return of((String) null);
67 }
68
69 public static <T> DataVariable<T> of(@Nullable String name, Class<T> type) {
70 return new DataVariable<>(name, type);
71 }
72
73 public static <T> DataVariable<T> of(Class<T> type) {
74 return of(null, type);
75 }
76}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolConstantTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolConstantTerm.java
new file mode 100644
index 00000000..5079f1ce
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolConstantTerm.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.term.bool;
2
3import tools.refinery.store.query.term.ConstantTerm;
4
5public final class BoolConstantTerm {
6 public static final ConstantTerm<Boolean> TRUE = new ConstantTerm<>(Boolean.class, true);
7 public static final ConstantTerm<Boolean> FALSE = new ConstantTerm<>(Boolean.class, false);
8
9 private BoolConstantTerm() {
10 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
11 }
12
13 public static ConstantTerm<Boolean> valueOf(boolean boolValue) {
14 return boolValue ? TRUE : FALSE;
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolLogicBinaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolLogicBinaryTerm.java
new file mode 100644
index 00000000..d85f864d
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolLogicBinaryTerm.java
@@ -0,0 +1,78 @@
1package tools.refinery.store.query.term.bool;
2
3import tools.refinery.store.query.equality.LiteralEqualityHelper;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.term.*;
6
7import java.util.Objects;
8
9public class BoolLogicBinaryTerm extends BinaryTerm<Boolean, Boolean, Boolean> {
10 private final LogicBinaryOperator operator;
11
12 protected BoolLogicBinaryTerm(LogicBinaryOperator operator, Term<Boolean> left, Term<Boolean> right) {
13 super(left, right);
14 this.operator = operator;
15 }
16
17 @Override
18 public Class<Boolean> getType() {
19 return Boolean.class;
20 }
21
22 @Override
23 public Class<Boolean> getLeftType() {
24 return getType();
25 }
26
27 @Override
28 public Class<Boolean> getRightType() {
29 return getType();
30 }
31
32 public LogicBinaryOperator getOperator() {
33 return operator;
34 }
35
36 @Override
37 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, AnyTerm other) {
38 if (!super.equalsWithSubstitution(helper, other)) {
39 return false;
40 }
41 var otherBoolLogicBinaryTerm = (BoolLogicBinaryTerm) other;
42 return operator == otherBoolLogicBinaryTerm.operator;
43 }
44
45 @Override
46 public Term<Boolean> doSubstitute(Substitution substitution, Term<Boolean> substitutedLeft,
47 Term<Boolean> substitutedRight) {
48 return new BoolLogicBinaryTerm(getOperator(), substitutedLeft, substitutedRight);
49 }
50
51 @Override
52 protected Boolean doEvaluate(Boolean leftValue, Boolean rightValue) {
53 return switch (getOperator()) {
54 case AND -> leftValue && rightValue;
55 case OR -> leftValue || rightValue;
56 case XOR -> leftValue ^ rightValue;
57 };
58 }
59
60 @Override
61 public String toString() {
62 return operator.formatString(getLeft().toString(), getRight().toString());
63 }
64
65 @Override
66 public boolean equals(Object o) {
67 if (this == o) return true;
68 if (o == null || getClass() != o.getClass()) return false;
69 if (!super.equals(o)) return false;
70 BoolLogicBinaryTerm that = (BoolLogicBinaryTerm) o;
71 return operator == that.operator;
72 }
73
74 @Override
75 public int hashCode() {
76 return Objects.hash(super.hashCode(), operator);
77 }
78}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolNotTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolNotTerm.java
new file mode 100644
index 00000000..855139b5
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolNotTerm.java
@@ -0,0 +1,36 @@
1package tools.refinery.store.query.term.bool;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.Term;
5import tools.refinery.store.query.term.UnaryTerm;
6
7public class BoolNotTerm extends UnaryTerm<Boolean, Boolean> {
8 protected BoolNotTerm(Term<Boolean> body) {
9 super(body);
10 }
11
12 @Override
13 public Class<Boolean> getType() {
14 return Boolean.class;
15 }
16
17 @Override
18 public Class<Boolean> getBodyType() {
19 return getType();
20 }
21
22 @Override
23 protected Term<Boolean> doSubstitute(Substitution substitution, Term<Boolean> substitutedBody) {
24 return new BoolNotTerm(substitutedBody);
25 }
26
27 @Override
28 protected Boolean doEvaluate(Boolean bodyValue) {
29 return !bodyValue;
30 }
31
32 @Override
33 public String toString() {
34 return "!(%s)".formatted(getBody());
35 }
36}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolTerms.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolTerms.java
new file mode 100644
index 00000000..3d6c8d9d
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/BoolTerms.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.term.bool;
2
3import tools.refinery.store.query.term.ConstantTerm;
4import tools.refinery.store.query.term.Term;
5
6public final class BoolTerms {
7 private BoolTerms() {
8 throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly");
9 }
10
11 public static ConstantTerm<Boolean> constant(boolean value) {
12 return BoolConstantTerm.valueOf(value);
13 }
14
15 public static BoolNotTerm not(Term<Boolean> body) {
16 return new BoolNotTerm(body);
17 }
18
19 public static BoolLogicBinaryTerm and(Term<Boolean> left, Term<Boolean> right) {
20 return new BoolLogicBinaryTerm(LogicBinaryOperator.AND, left, right);
21 }
22
23 public static BoolLogicBinaryTerm or(Term<Boolean> left, Term<Boolean> right) {
24 return new BoolLogicBinaryTerm(LogicBinaryOperator.OR, left, right);
25 }
26
27 public static BoolLogicBinaryTerm xor(Term<Boolean> left, Term<Boolean> right) {
28 return new BoolLogicBinaryTerm(LogicBinaryOperator.XOR, left, right);
29 }
30}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/LogicBinaryOperator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/LogicBinaryOperator.java
new file mode 100644
index 00000000..ca9ac66e
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/bool/LogicBinaryOperator.java
@@ -0,0 +1,17 @@
1package tools.refinery.store.query.term.bool;
2
3public enum LogicBinaryOperator {
4 AND("&&"),
5 OR("||"),
6 XOR("^^");
7
8 private final String text;
9
10 LogicBinaryOperator(String text) {
11 this.text = text;
12 }
13
14 public String formatString(String left, String right) {
15 return "(%s) %s (%s)".formatted(left, text, right);
16 }
17}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticBinaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticBinaryTerm.java
new file mode 100644
index 00000000..32e41718
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticBinaryTerm.java
@@ -0,0 +1,48 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.ArithmeticBinaryOperator;
5import tools.refinery.store.query.term.ArithmeticBinaryTerm;
6import tools.refinery.store.query.term.Term;
7
8public class IntArithmeticBinaryTerm extends ArithmeticBinaryTerm<Integer> {
9 public IntArithmeticBinaryTerm(ArithmeticBinaryOperator operator, Term<Integer> left, Term<Integer> right) {
10 super(operator, left, right);
11 }
12
13 @Override
14 public Class<Integer> getType() {
15 return Integer.class;
16 }
17
18 @Override
19 public Term<Integer> doSubstitute(Substitution substitution, Term<Integer> substitutedLeft,
20 Term<Integer> substitutedRight) {
21 return new IntArithmeticBinaryTerm(getOperator(), substitutedLeft, substitutedRight);
22 }
23
24 @Override
25 protected Integer doEvaluate(Integer leftValue, Integer rightValue) {
26 return switch (getOperator()) {
27 case ADD -> leftValue + rightValue;
28 case SUB -> leftValue - rightValue;
29 case MUL -> leftValue * rightValue;
30 case DIV -> rightValue == 0 ? null : leftValue / rightValue;
31 case POW -> rightValue < 0 ? null : power(leftValue, rightValue);
32 case MIN -> Math.min(leftValue, rightValue);
33 case MAX -> Math.max(leftValue, rightValue);
34 };
35 }
36
37 private static int power(int base, int exponent) {
38 int accum = 1;
39 while (exponent > 0) {
40 if (exponent % 2 == 1) {
41 accum = accum * base;
42 }
43 base = base * base;
44 exponent = exponent / 2;
45 }
46 return accum;
47 }
48}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticUnaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticUnaryTerm.java
new file mode 100644
index 00000000..1e769259
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntArithmeticUnaryTerm.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.Term;
5import tools.refinery.store.query.term.ArithmeticUnaryOperator;
6import tools.refinery.store.query.term.ArithmeticUnaryTerm;
7
8public class IntArithmeticUnaryTerm extends ArithmeticUnaryTerm<Integer> {
9 public IntArithmeticUnaryTerm(ArithmeticUnaryOperator operation, Term<Integer> body) {
10 super(operation, body);
11 }
12
13 @Override
14 public Class<Integer> getType() {
15 return Integer.class;
16 }
17
18 @Override
19 protected Term<Integer> doSubstitute(Substitution substitution, Term<Integer> substitutedBody) {
20 return new IntArithmeticUnaryTerm(getOperator(), substitutedBody);
21 }
22
23 @Override
24 protected Integer doEvaluate(Integer bodyValue) {
25 return switch(getOperator()) {
26 case PLUS -> bodyValue;
27 case MINUS -> -bodyValue;
28 };
29 }
30}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntComparisonTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntComparisonTerm.java
new file mode 100644
index 00000000..322d2b80
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntComparisonTerm.java
@@ -0,0 +1,34 @@
1package tools.refinery.store.query.term.int_;
2import tools.refinery.store.query.substitution.Substitution;
3import tools.refinery.store.query.term.ComparisonOperator;
4import tools.refinery.store.query.term.ComparisonTerm;
5import tools.refinery.store.query.term.Term;
6
7public class IntComparisonTerm extends ComparisonTerm<Integer> {
8 public IntComparisonTerm(ComparisonOperator operator, Term<Integer> left, Term<Integer> right) {
9 super(operator, left, right);
10 }
11
12 @Override
13 public Class<Integer> getOperandType() {
14 return Integer.class;
15 }
16
17 @Override
18 public Term<Boolean> doSubstitute(Substitution substitution, Term<Integer> substitutedLeft,
19 Term<Integer> substitutedRight) {
20 return new IntComparisonTerm(getOperator(), substitutedLeft, substitutedRight);
21 }
22
23 @Override
24 protected Boolean doEvaluate(Integer leftValue, Integer rightValue) {
25 return switch (getOperator()) {
26 case EQ -> leftValue.equals(rightValue);
27 case NOT_EQ -> !leftValue.equals(rightValue);
28 case LESS -> leftValue < rightValue;
29 case LESS_EQ -> leftValue <= rightValue;
30 case GREATER -> leftValue > rightValue;
31 case GREATER_EQ -> leftValue >= rightValue;
32 };
33 }
34}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntExtremeValueAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntExtremeValueAggregator.java
new file mode 100644
index 00000000..d5a6add0
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntExtremeValueAggregator.java
@@ -0,0 +1,17 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.term.ExtremeValueAggregator;
4
5import java.util.Comparator;
6
7public final class IntExtremeValueAggregator {
8 public static final ExtremeValueAggregator<Integer> MINIMUM = new ExtremeValueAggregator<>(Integer.class,
9 Integer.MAX_VALUE);
10
11 public static final ExtremeValueAggregator<Integer> MAXIMUM = new ExtremeValueAggregator<>(Integer.class,
12 Integer.MIN_VALUE, Comparator.reverseOrder());
13
14 private IntExtremeValueAggregator() {
15 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
16 }
17}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntSumAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntSumAggregator.java
new file mode 100644
index 00000000..65024f52
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntSumAggregator.java
@@ -0,0 +1,35 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.term.StatelessAggregator;
4
5public final class IntSumAggregator implements StatelessAggregator<Integer, Integer> {
6 public static final IntSumAggregator INSTANCE = new IntSumAggregator();
7
8 private IntSumAggregator() {
9 }
10
11 @Override
12 public Class<Integer> getResultType() {
13 return Integer.class;
14 }
15
16 @Override
17 public Class<Integer> getInputType() {
18 return Integer.class;
19 }
20
21 @Override
22 public Integer getEmptyResult() {
23 return 0;
24 }
25
26 @Override
27 public Integer add(Integer current, Integer value) {
28 return current + value;
29 }
30
31 @Override
32 public Integer remove(Integer current, Integer value) {
33 return current - value;
34 }
35}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntTerms.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntTerms.java
new file mode 100644
index 00000000..86594deb
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/IntTerms.java
@@ -0,0 +1,81 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.term.*;
4
5public final class IntTerms {
6 public static final Aggregator<Integer, Integer> INT_SUM = IntSumAggregator.INSTANCE;
7 public static final Aggregator<Integer, Integer> INT_MIN = IntExtremeValueAggregator.MINIMUM;
8 public static final Aggregator<Integer, Integer> INT_MAX = IntExtremeValueAggregator.MAXIMUM;
9
10 private IntTerms() {
11 throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly");
12 }
13
14 public static ConstantTerm<Integer> constant(int value) {
15 return new ConstantTerm<>(Integer.class, value);
16 }
17
18 public static IntArithmeticUnaryTerm plus(Term<Integer> body) {
19 return new IntArithmeticUnaryTerm(ArithmeticUnaryOperator.PLUS, body);
20 }
21
22 public static IntArithmeticUnaryTerm minus(Term<Integer> body) {
23 return new IntArithmeticUnaryTerm(ArithmeticUnaryOperator.MINUS, body);
24 }
25
26 public static IntArithmeticBinaryTerm add(Term<Integer> left, Term<Integer> right) {
27 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.ADD, left, right);
28 }
29
30 public static IntArithmeticBinaryTerm sub(Term<Integer> left, Term<Integer> right) {
31 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.SUB, left, right);
32 }
33
34 public static IntArithmeticBinaryTerm mul(Term<Integer> left, Term<Integer> right) {
35 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.MUL, left, right);
36 }
37
38 public static IntArithmeticBinaryTerm div(Term<Integer> left, Term<Integer> right) {
39 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.DIV, left, right);
40 }
41
42 public static IntArithmeticBinaryTerm pow(Term<Integer> left, Term<Integer> right) {
43 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.POW, left, right);
44 }
45
46 public static IntArithmeticBinaryTerm min(Term<Integer> left, Term<Integer> right) {
47 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.MIN, left, right);
48 }
49
50 public static IntArithmeticBinaryTerm max(Term<Integer> left, Term<Integer> right) {
51 return new IntArithmeticBinaryTerm(ArithmeticBinaryOperator.MAX, left, right);
52 }
53
54 public static IntComparisonTerm eq(Term<Integer> left, Term<Integer> right) {
55 return new IntComparisonTerm(ComparisonOperator.EQ, left, right);
56 }
57
58 public static IntComparisonTerm notEq(Term<Integer> left, Term<Integer> right) {
59 return new IntComparisonTerm(ComparisonOperator.NOT_EQ, left, right);
60 }
61
62 public static IntComparisonTerm less(Term<Integer> left, Term<Integer> right) {
63 return new IntComparisonTerm(ComparisonOperator.LESS, left, right);
64 }
65
66 public static IntComparisonTerm lessEq(Term<Integer> left, Term<Integer> right) {
67 return new IntComparisonTerm(ComparisonOperator.LESS_EQ, left, right);
68 }
69
70 public static IntComparisonTerm greater(Term<Integer> left, Term<Integer> right) {
71 return new IntComparisonTerm(ComparisonOperator.GREATER, left, right);
72 }
73
74 public static IntComparisonTerm greaterEq(Term<Integer> left, Term<Integer> right) {
75 return new IntComparisonTerm(ComparisonOperator.GREATER_EQ, left, right);
76 }
77
78 public static RealToIntTerm asInt(Term<Double> body) {
79 return new RealToIntTerm(body);
80 }
81}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/RealToIntTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/RealToIntTerm.java
new file mode 100644
index 00000000..53875ddc
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/int_/RealToIntTerm.java
@@ -0,0 +1,36 @@
1package tools.refinery.store.query.term.int_;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.Term;
5import tools.refinery.store.query.term.UnaryTerm;
6
7public class RealToIntTerm extends UnaryTerm<Integer, Double> {
8 protected RealToIntTerm(Term<Double> body) {
9 super(body);
10 }
11
12 @Override
13 public Class<Integer> getType() {
14 return Integer.class;
15 }
16
17 @Override
18 public Class<Double> getBodyType() {
19 return Double.class;
20 }
21
22 @Override
23 protected Integer doEvaluate(Double bodyValue) {
24 return bodyValue.intValue();
25 }
26
27 @Override
28 protected Term<Integer> doSubstitute(Substitution substitution, Term<Double> substitutedBody) {
29 return new RealToIntTerm(substitutedBody);
30 }
31
32 @Override
33 public String toString() {
34 return "(%s) as int".formatted(getBody());
35 }
36}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/IntToRealTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/IntToRealTerm.java
new file mode 100644
index 00000000..55590824
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/IntToRealTerm.java
@@ -0,0 +1,36 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.Term;
5import tools.refinery.store.query.term.UnaryTerm;
6
7public class IntToRealTerm extends UnaryTerm<Double, Integer> {
8 protected IntToRealTerm(Term<Integer> body) {
9 super(body);
10 }
11
12 @Override
13 public Class<Double> getType() {
14 return Double.class;
15 }
16
17 @Override
18 public Class<Integer> getBodyType() {
19 return Integer.class;
20 }
21
22 @Override
23 protected Term<Double> doSubstitute(Substitution substitution, Term<Integer> substitutedBody) {
24 return new IntToRealTerm(substitutedBody);
25 }
26
27 @Override
28 protected Double doEvaluate(Integer bodyValue) {
29 return bodyValue.doubleValue();
30 }
31
32 @Override
33 public String toString() {
34 return "(%s) as real".formatted(getBody());
35 }
36}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticBinaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticBinaryTerm.java
new file mode 100644
index 00000000..57bcbe5e
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticBinaryTerm.java
@@ -0,0 +1,36 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.ArithmeticBinaryOperator;
5import tools.refinery.store.query.term.ArithmeticBinaryTerm;
6import tools.refinery.store.query.term.Term;
7
8public class RealArithmeticBinaryTerm extends ArithmeticBinaryTerm<Double> {
9 public RealArithmeticBinaryTerm(ArithmeticBinaryOperator operator, Term<Double> left, Term<Double> right) {
10 super(operator, left, right);
11 }
12
13 @Override
14 public Class<Double> getType() {
15 return Double.class;
16 }
17
18 @Override
19 public Term<Double> doSubstitute(Substitution substitution, Term<Double> substitutedLeft,
20 Term<Double> substitutedRight) {
21 return new RealArithmeticBinaryTerm(getOperator(), substitutedLeft, substitutedRight);
22 }
23
24 @Override
25 protected Double doEvaluate(Double leftValue, Double rightValue) {
26 return switch (getOperator()) {
27 case ADD -> leftValue + rightValue;
28 case SUB -> leftValue - rightValue;
29 case MUL -> leftValue * rightValue;
30 case DIV -> leftValue / rightValue;
31 case POW -> Math.pow(leftValue, rightValue);
32 case MIN -> Math.min(leftValue, rightValue);
33 case MAX -> Math.max(leftValue, rightValue);
34 };
35 }
36}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticUnaryTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticUnaryTerm.java
new file mode 100644
index 00000000..632e68bf
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealArithmeticUnaryTerm.java
@@ -0,0 +1,30 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.ArithmeticUnaryOperator;
5import tools.refinery.store.query.term.ArithmeticUnaryTerm;
6import tools.refinery.store.query.term.Term;
7
8public class RealArithmeticUnaryTerm extends ArithmeticUnaryTerm<Double> {
9 public RealArithmeticUnaryTerm(ArithmeticUnaryOperator operation, Term<Double> body) {
10 super(operation, body);
11 }
12
13 @Override
14 public Class<Double> getType() {
15 return Double.class;
16 }
17
18 @Override
19 protected Term<Double> doSubstitute(Substitution substitution, Term<Double> substitutedBody) {
20 return new RealArithmeticUnaryTerm(getOperator(), substitutedBody);
21 }
22
23 @Override
24 protected Double doEvaluate(Double bodyValue) {
25 return switch(getOperator()) {
26 case PLUS -> bodyValue;
27 case MINUS -> -bodyValue;
28 };
29 }
30}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealComparisonTerm.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealComparisonTerm.java
new file mode 100644
index 00000000..75d97adb
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealComparisonTerm.java
@@ -0,0 +1,35 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.ComparisonOperator;
5import tools.refinery.store.query.term.ComparisonTerm;
6import tools.refinery.store.query.term.Term;
7
8public class RealComparisonTerm extends ComparisonTerm<Double> {
9 public RealComparisonTerm(ComparisonOperator operator, Term<Double> left, Term<Double> right) {
10 super(operator, left, right);
11 }
12
13 @Override
14 public Class<Double> getOperandType() {
15 return Double.class;
16 }
17
18 @Override
19 public Term<Boolean> doSubstitute(Substitution substitution, Term<Double> substitutedLeft,
20 Term<Double> substitutedRight) {
21 return new RealComparisonTerm(getOperator(), substitutedLeft, substitutedRight);
22 }
23
24 @Override
25 protected Boolean doEvaluate(Double leftValue, Double rightValue) {
26 return switch (getOperator()) {
27 case EQ -> leftValue.equals(rightValue);
28 case NOT_EQ -> !leftValue.equals(rightValue);
29 case LESS -> leftValue < rightValue;
30 case LESS_EQ -> leftValue <= rightValue;
31 case GREATER -> leftValue > rightValue;
32 case GREATER_EQ -> leftValue >= rightValue;
33 };
34 }
35}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealExtremeValueAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealExtremeValueAggregator.java
new file mode 100644
index 00000000..23384530
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealExtremeValueAggregator.java
@@ -0,0 +1,17 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.term.ExtremeValueAggregator;
4
5import java.util.Comparator;
6
7public final class RealExtremeValueAggregator {
8 public static final ExtremeValueAggregator<Double> MINIMUM = new ExtremeValueAggregator<>(Double.class,
9 Double.POSITIVE_INFINITY);
10
11 public static final ExtremeValueAggregator<Double> MAXIMUM = new ExtremeValueAggregator<>(Double.class,
12 Double.NEGATIVE_INFINITY, Comparator.reverseOrder());
13
14 private RealExtremeValueAggregator() {
15 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
16 }
17}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealSumAggregator.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealSumAggregator.java
new file mode 100644
index 00000000..d5888664
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealSumAggregator.java
@@ -0,0 +1,85 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.term.StatefulAggregate;
4import tools.refinery.store.query.term.StatefulAggregator;
5
6import java.util.Map;
7import java.util.TreeMap;
8
9public final class RealSumAggregator implements StatefulAggregator<Double, Double> {
10 public static final RealSumAggregator INSTANCE = new RealSumAggregator();
11
12 private RealSumAggregator() {
13 }
14
15 @Override
16 public Class<Double> getResultType() {
17 return null;
18 }
19
20 @Override
21 public Class<Double> getInputType() {
22 return null;
23 }
24
25 @Override
26 public StatefulAggregate<Double, Double> createEmptyAggregate() {
27 return new Aggregate();
28 }
29
30 @Override
31 public Double getEmptyResult() {
32 return 0d;
33 }
34
35 private static class Aggregate implements StatefulAggregate<Double, Double> {
36 private final Map<Double, Integer> values;
37
38 public Aggregate() {
39 values = new TreeMap<>();
40 }
41
42 private Aggregate(Aggregate other) {
43 values = new TreeMap<>(other.values);
44 }
45
46 @Override
47 public void add(Double value) {
48 values.compute(value, (ignoredValue, currentCount) -> currentCount == null ? 1 : currentCount + 1);
49 }
50
51 @Override
52 public void remove(Double value) {
53 values.compute(value, (theValue, currentCount) -> {
54 if (currentCount == null || currentCount <= 0) {
55 throw new IllegalStateException("Invalid count %d for value %f".formatted(currentCount, theValue));
56 }
57 return currentCount.equals(1) ? null : currentCount - 1;
58 });
59 }
60
61 @Override
62 public Double getResult() {
63 return values.entrySet()
64 .stream()
65 .mapToDouble(entry -> entry.getKey() * entry.getValue())
66 .reduce(Double::sum)
67 .orElse(0d);
68 }
69
70 @Override
71 public boolean isEmpty() {
72 return values.isEmpty();
73 }
74
75 @Override
76 public StatefulAggregate<Double, Double> deepCopy() {
77 return new Aggregate(this);
78 }
79
80 @Override
81 public boolean contains(Double value) {
82 return values.containsKey(value);
83 }
84 }
85}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealTerms.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealTerms.java
new file mode 100644
index 00000000..a8117842
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/term/real/RealTerms.java
@@ -0,0 +1,81 @@
1package tools.refinery.store.query.term.real;
2
3import tools.refinery.store.query.term.*;
4
5public final class RealTerms {
6 public static final Aggregator<Double, Double> REAL_SUM = RealSumAggregator.INSTANCE;
7 public static final Aggregator<Double, Double> REAL_MIN = RealExtremeValueAggregator.MINIMUM;
8 public static final Aggregator<Double, Double> REAL_MAX = RealExtremeValueAggregator.MAXIMUM;
9
10 private RealTerms() {
11 throw new IllegalArgumentException("This is a static utility class and should not be instantiated directly");
12 }
13
14 public static ConstantTerm<Double> constant(double value) {
15 return new ConstantTerm<>(Double.class, value);
16 }
17
18 public static RealArithmeticUnaryTerm plus(Term<Double> body) {
19 return new RealArithmeticUnaryTerm(ArithmeticUnaryOperator.PLUS, body);
20 }
21
22 public static RealArithmeticUnaryTerm minus(Term<Double> body) {
23 return new RealArithmeticUnaryTerm(ArithmeticUnaryOperator.MINUS, body);
24 }
25
26 public static RealArithmeticBinaryTerm add(Term<Double> left, Term<Double> right) {
27 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.ADD, left, right);
28 }
29
30 public static RealArithmeticBinaryTerm sub(Term<Double> left, Term<Double> right) {
31 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.SUB, left, right);
32 }
33
34 public static RealArithmeticBinaryTerm mul(Term<Double> left, Term<Double> right) {
35 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.MUL, left, right);
36 }
37
38 public static RealArithmeticBinaryTerm div(Term<Double> left, Term<Double> right) {
39 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.DIV, left, right);
40 }
41
42 public static RealArithmeticBinaryTerm pow(Term<Double> left, Term<Double> right) {
43 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.POW, left, right);
44 }
45
46 public static RealArithmeticBinaryTerm min(Term<Double> left, Term<Double> right) {
47 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.MIN, left, right);
48 }
49
50 public static RealArithmeticBinaryTerm max(Term<Double> left, Term<Double> right) {
51 return new RealArithmeticBinaryTerm(ArithmeticBinaryOperator.MAX, left, right);
52 }
53
54 public static RealComparisonTerm eq(Term<Double> left, Term<Double> right) {
55 return new RealComparisonTerm(ComparisonOperator.EQ, left, right);
56 }
57
58 public static RealComparisonTerm notEq(Term<Double> left, Term<Double> right) {
59 return new RealComparisonTerm(ComparisonOperator.NOT_EQ, left, right);
60 }
61
62 public static RealComparisonTerm less(Term<Double> left, Term<Double> right) {
63 return new RealComparisonTerm(ComparisonOperator.LESS, left, right);
64 }
65
66 public static RealComparisonTerm lessEq(Term<Double> left, Term<Double> right) {
67 return new RealComparisonTerm(ComparisonOperator.LESS_EQ, left, right);
68 }
69
70 public static RealComparisonTerm greater(Term<Double> left, Term<Double> right) {
71 return new RealComparisonTerm(ComparisonOperator.GREATER, left, right);
72 }
73
74 public static RealComparisonTerm greaterEq(Term<Double> left, Term<Double> right) {
75 return new RealComparisonTerm(ComparisonOperator.GREATER_EQ, left, right);
76 }
77
78 public static IntToRealTerm asReal(Term<Integer> body) {
79 return new IntToRealTerm(body);
80 }
81}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/RestrictedValuation.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/RestrictedValuation.java
new file mode 100644
index 00000000..fb512d88
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/RestrictedValuation.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.valuation;
2
3import tools.refinery.store.query.term.AnyDataVariable;
4import tools.refinery.store.query.term.DataVariable;
5
6import java.util.Set;
7
8public record RestrictedValuation(Valuation valuation, Set<AnyDataVariable> allowedVariables) implements Valuation {
9 @Override
10 public <T> T getValue(DataVariable<T> variable) {
11 if (!allowedVariables.contains(variable)) {
12 throw new IllegalArgumentException("Variable %s is not in scope".formatted(variable));
13 }
14 return valuation.getValue(variable);
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/SubstitutedValuation.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/SubstitutedValuation.java
new file mode 100644
index 00000000..8e79663c
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/SubstitutedValuation.java
@@ -0,0 +1,11 @@
1package tools.refinery.store.query.valuation;
2
3import tools.refinery.store.query.substitution.Substitution;
4import tools.refinery.store.query.term.DataVariable;
5
6public record SubstitutedValuation(Valuation originalValuation, Substitution substitution) implements Valuation {
7 @Override
8 public <T> T getValue(DataVariable<T> variable) {
9 return originalValuation.getValue(substitution.getTypeSafeSubstitute(variable));
10 }
11}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/Valuation.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/Valuation.java
new file mode 100644
index 00000000..3ba9a6b8
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/valuation/Valuation.java
@@ -0,0 +1,23 @@
1package tools.refinery.store.query.valuation;
2
3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.query.substitution.Substitution;
5import tools.refinery.store.query.term.AnyDataVariable;
6import tools.refinery.store.query.term.DataVariable;
7
8import java.util.Set;
9
10public interface Valuation {
11 <T> T getValue(DataVariable<T> variable);
12
13 default Valuation substitute(@Nullable Substitution substitution) {
14 if (substitution == null) {
15 return this;
16 }
17 return new SubstitutedValuation(this, substitution);
18 }
19
20 default Valuation restrict(Set<? extends AnyDataVariable> allowedVariables) {
21 return new RestrictedValuation(this, Set.copyOf(allowedVariables));
22 }
23}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/AnyRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/AnyRelationView.java
index bc3ac1ea..6ae410f2 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/AnyRelationView.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/AnyRelationView.java
@@ -1,13 +1,13 @@
1package tools.refinery.store.query.view; 1package tools.refinery.store.query.view;
2 2
3import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.FunctionalDependency; 4import tools.refinery.store.query.dnf.FunctionalDependency;
5import tools.refinery.store.representation.AnySymbol; 5import tools.refinery.store.representation.AnySymbol;
6import tools.refinery.store.query.RelationLike; 6import tools.refinery.store.query.Constraint;
7 7
8import java.util.Set; 8import java.util.Set;
9 9
10public sealed interface AnyRelationView extends RelationLike permits RelationView { 10public sealed interface AnyRelationView extends Constraint permits RelationView {
11 AnySymbol getSymbol(); 11 AnySymbol getSymbol();
12 12
13 String getViewName(); 13 String getViewName();
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/ForbiddenRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/ForbiddenRelationView.java
new file mode 100644
index 00000000..050b9496
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/ForbiddenRelationView.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.view;
2
3import tools.refinery.store.representation.Symbol;
4import tools.refinery.store.representation.TruthValue;
5import tools.refinery.store.tuple.Tuple;
6
7public class ForbiddenRelationView extends TuplePreservingRelationView<TruthValue> {
8 public ForbiddenRelationView(Symbol<TruthValue> symbol) {
9 super(symbol, "forbidden");
10 }
11
12 @Override
13 public boolean filter(Tuple key, TruthValue value) {
14 return !value.may();
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
index 3d278a8b..7ec9e7ac 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/FunctionalRelationView.java
@@ -1,22 +1,31 @@
1package tools.refinery.store.query.view; 1package tools.refinery.store.query.view;
2 2
3import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.FunctionalDependency; 4import tools.refinery.store.query.dnf.FunctionalDependency;
5import tools.refinery.store.query.term.DataSort;
6import tools.refinery.store.query.term.NodeSort;
7import tools.refinery.store.query.term.Sort;
5import tools.refinery.store.representation.Symbol; 8import tools.refinery.store.representation.Symbol;
6import tools.refinery.store.tuple.Tuple; 9import tools.refinery.store.tuple.Tuple;
7import tools.refinery.store.tuple.Tuple1; 10import tools.refinery.store.tuple.Tuple1;
8 11
12import java.util.List;
13import java.util.Objects;
9import java.util.Set; 14import java.util.Set;
10import java.util.stream.Collectors; 15import java.util.stream.Collectors;
11import java.util.stream.IntStream; 16import java.util.stream.IntStream;
12 17
13public final class FunctionalRelationView<T> extends RelationView<T> { 18public final class FunctionalRelationView<T> extends RelationView<T> {
19 private final T defaultValue;
20
14 public FunctionalRelationView(Symbol<T> symbol, String name) { 21 public FunctionalRelationView(Symbol<T> symbol, String name) {
15 super(symbol, name); 22 super(symbol, name);
23 defaultValue = symbol.defaultValue();
16 } 24 }
17 25
18 public FunctionalRelationView(Symbol<T> symbol) { 26 public FunctionalRelationView(Symbol<T> symbol) {
19 super(symbol); 27 super(symbol);
28 defaultValue = symbol.defaultValue();
20 } 29 }
21 30
22 @Override 31 @Override
@@ -37,7 +46,7 @@ public final class FunctionalRelationView<T> extends RelationView<T> {
37 46
38 @Override 47 @Override
39 public boolean filter(Tuple key, T value) { 48 public boolean filter(Tuple key, T value) {
40 return true; 49 return !Objects.equals(defaultValue, value);
41 } 50 }
42 51
43 @Override 52 @Override
@@ -68,4 +77,29 @@ public final class FunctionalRelationView<T> extends RelationView<T> {
68 public int arity() { 77 public int arity() {
69 return getSymbol().arity() + 1; 78 return getSymbol().arity() + 1;
70 } 79 }
80
81 @Override
82 public List<Sort> getSorts() {
83 var sorts = new Sort[arity()];
84 int valueIndex = sorts.length - 1;
85 for (int i = 0; i < valueIndex; i++) {
86 sorts[i] = NodeSort.INSTANCE;
87 }
88 sorts[valueIndex] = new DataSort<>(getSymbol().valueType());
89 return List.of(sorts);
90 }
91
92 @Override
93 public boolean equals(Object o) {
94 if (this == o) return true;
95 if (o == null || getClass() != o.getClass()) return false;
96 if (!super.equals(o)) return false;
97 FunctionalRelationView<?> that = (FunctionalRelationView<?>) o;
98 return Objects.equals(defaultValue, that.defaultValue);
99 }
100
101 @Override
102 public int hashCode() {
103 return Objects.hash(super.hashCode(), defaultValue);
104 }
71} 105}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MayRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MayRelationView.java
new file mode 100644
index 00000000..a2a84b3c
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MayRelationView.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.view;
2
3import tools.refinery.store.representation.Symbol;
4import tools.refinery.store.representation.TruthValue;
5import tools.refinery.store.tuple.Tuple;
6
7public class MayRelationView extends TuplePreservingRelationView<TruthValue> {
8 public MayRelationView(Symbol<TruthValue> symbol) {
9 super(symbol, "may");
10 }
11
12 @Override
13 public boolean filter(Tuple key, TruthValue value) {
14 return value.may();
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MustRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MustRelationView.java
new file mode 100644
index 00000000..72ac0ca3
--- /dev/null
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/MustRelationView.java
@@ -0,0 +1,16 @@
1package tools.refinery.store.query.view;
2
3import tools.refinery.store.representation.Symbol;
4import tools.refinery.store.representation.TruthValue;
5import tools.refinery.store.tuple.Tuple;
6
7public class MustRelationView extends TuplePreservingRelationView<TruthValue> {
8 public MustRelationView(Symbol<TruthValue> symbol) {
9 super(symbol, "must");
10 }
11
12 @Override
13 public boolean filter(Tuple key, TruthValue value) {
14 return value.must();
15 }
16}
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/RelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/RelationView.java
index ea9fd5e2..d7164b3b 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/RelationView.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/RelationView.java
@@ -1,14 +1,10 @@
1package tools.refinery.store.query.view; 1package tools.refinery.store.query.view;
2 2
3import tools.refinery.store.query.Variable;
4import tools.refinery.store.map.CursorAsIterator; 3import tools.refinery.store.map.CursorAsIterator;
5import tools.refinery.store.model.Model; 4import tools.refinery.store.model.Model;
6import tools.refinery.store.query.literal.CallPolarity;
7import tools.refinery.store.query.literal.RelationViewLiteral;
8import tools.refinery.store.representation.Symbol; 5import tools.refinery.store.representation.Symbol;
9import tools.refinery.store.tuple.Tuple; 6import tools.refinery.store.tuple.Tuple;
10 7
11import java.util.List;
12import java.util.Objects; 8import java.util.Objects;
13import java.util.UUID; 9import java.util.UUID;
14 10
@@ -56,20 +52,14 @@ public abstract non-sealed class RelationView<T> implements AnyRelationView {
56 return (() -> new CursorAsIterator<>(model.getInterpretation(symbol).getAll(), this::forwardMap, this::filter)); 52 return (() -> new CursorAsIterator<>(model.getInterpretation(symbol).getAll(), this::forwardMap, this::filter));
57 } 53 }
58 54
59 public RelationViewLiteral call(CallPolarity polarity, List<Variable> arguments) { 55 @Override
60 return new RelationViewLiteral(polarity, this, arguments); 56 public String toString() {
61 } 57 return name();
62
63 public RelationViewLiteral call(CallPolarity polarity, Variable... arguments) {
64 return call(polarity, List.of(arguments));
65 }
66
67 public RelationViewLiteral call(Variable... arguments) {
68 return call(CallPolarity.POSITIVE, arguments);
69 } 58 }
70 59
71 public RelationViewLiteral callTransitive(Variable left, Variable right) { 60 @Override
72 return call(CallPolarity.TRANSITIVE, List.of(left, right)); 61 public String toReferenceString() {
62 return "@RelationView(\"%s\") %s".formatted(viewName, symbol.name());
73 } 63 }
74 64
75 @Override 65 @Override
diff --git a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/TuplePreservingRelationView.java b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/TuplePreservingRelationView.java
index 8cc4986e..234b3a9a 100644
--- a/subprojects/store-query/src/main/java/tools/refinery/store/query/view/TuplePreservingRelationView.java
+++ b/subprojects/store-query/src/main/java/tools/refinery/store/query/view/TuplePreservingRelationView.java
@@ -1,10 +1,15 @@
1package tools.refinery.store.query.view; 1package tools.refinery.store.query.view;
2 2
3import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.term.NodeSort;
5import tools.refinery.store.query.term.Sort;
4import tools.refinery.store.tuple.Tuple; 6import tools.refinery.store.tuple.Tuple;
5import tools.refinery.store.tuple.Tuple1; 7import tools.refinery.store.tuple.Tuple1;
6import tools.refinery.store.representation.Symbol; 8import tools.refinery.store.representation.Symbol;
7 9
10import java.util.Arrays;
11import java.util.List;
12
8public abstract class TuplePreservingRelationView<T> extends RelationView<T> { 13public abstract class TuplePreservingRelationView<T> extends RelationView<T> {
9 protected TuplePreservingRelationView(Symbol<T> symbol, String name) { 14 protected TuplePreservingRelationView(Symbol<T> symbol, String name) {
10 super(symbol, name); 15 super(symbol, name);
@@ -38,7 +43,15 @@ public abstract class TuplePreservingRelationView<T> extends RelationView<T> {
38 return filter(key, value); 43 return filter(key, value);
39 } 44 }
40 45
46 @Override
41 public int arity() { 47 public int arity() {
42 return this.getSymbol().arity(); 48 return this.getSymbol().arity();
43 } 49 }
50
51 @Override
52 public List<Sort> getSorts() {
53 var sorts = new Sort[arity()];
54 Arrays.fill(sorts, NodeSort.INSTANCE);
55 return List.of(sorts);
56 }
44} 57}
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/DnfBuilderTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/DnfBuilderTest.java
index e6701fe3..ceb46d6f 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/DnfBuilderTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/DnfBuilderTest.java
@@ -1,7 +1,9 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query;
2 2
3import org.junit.jupiter.api.Test; 3import org.junit.jupiter.api.Test;
4import tools.refinery.store.query.dnf.Dnf;
4import tools.refinery.store.query.literal.BooleanLiteral; 5import tools.refinery.store.query.literal.BooleanLiteral;
6import tools.refinery.store.query.term.Variable;
5import tools.refinery.store.query.view.KeyOnlyRelationView; 7import tools.refinery.store.query.view.KeyOnlyRelationView;
6import tools.refinery.store.representation.Symbol; 8import tools.refinery.store.representation.Symbol;
7 9
@@ -12,8 +14,8 @@ import static tools.refinery.store.query.tests.QueryMatchers.structurallyEqualTo
12class DnfBuilderTest { 14class DnfBuilderTest {
13 @Test 15 @Test
14 void eliminateTrueTest() { 16 void eliminateTrueTest() {
15 var p = new Variable("p"); 17 var p = Variable.of("p");
16 var q = new Variable("q"); 18 var q = Variable.of("q");
17 var friend = new Symbol<>("friend", 2, Boolean.class, false); 19 var friend = new Symbol<>("friend", 2, Boolean.class, false);
18 var friendView = new KeyOnlyRelationView<>(friend); 20 var friendView = new KeyOnlyRelationView<>(friend);
19 21
@@ -28,8 +30,8 @@ class DnfBuilderTest {
28 30
29 @Test 31 @Test
30 void eliminateFalseTest() { 32 void eliminateFalseTest() {
31 var p = new Variable("p"); 33 var p = Variable.of("p");
32 var q = new Variable("q"); 34 var q = Variable.of("q");
33 var friend = new Symbol<>("friend", 2, Boolean.class, false); 35 var friend = new Symbol<>("friend", 2, Boolean.class, false);
34 var friendView = new KeyOnlyRelationView<>(friend); 36 var friendView = new KeyOnlyRelationView<>(friend);
35 37
@@ -45,8 +47,8 @@ class DnfBuilderTest {
45 47
46 @Test 48 @Test
47 void alwaysTrueTest() { 49 void alwaysTrueTest() {
48 var p = new Variable("p"); 50 var p = Variable.of("p");
49 var q = new Variable("q"); 51 var q = Variable.of("q");
50 var friend = new Symbol<>("friend", 2, Boolean.class, false); 52 var friend = new Symbol<>("friend", 2, Boolean.class, false);
51 var friendView = new KeyOnlyRelationView<>(friend); 53 var friendView = new KeyOnlyRelationView<>(friend);
52 54
@@ -62,8 +64,8 @@ class DnfBuilderTest {
62 64
63 @Test 65 @Test
64 void alwaysFalseTest() { 66 void alwaysFalseTest() {
65 var p = new Variable("p"); 67 var p = Variable.of("p");
66 var q = new Variable("q"); 68 var q = Variable.of("q");
67 var friend = new Symbol<>("friend", 2, Boolean.class, false); 69 var friend = new Symbol<>("friend", 2, Boolean.class, false);
68 var friendView = new KeyOnlyRelationView<>(friend); 70 var friendView = new KeyOnlyRelationView<>(friend);
69 71
@@ -78,8 +80,8 @@ class DnfBuilderTest {
78 80
79 @Test 81 @Test
80 void eliminateTrueDnfTest() { 82 void eliminateTrueDnfTest() {
81 var p = new Variable("p"); 83 var p = Variable.of("p");
82 var q = new Variable("q"); 84 var q = Variable.of("q");
83 var friend = new Symbol<>("friend", 2, Boolean.class, false); 85 var friend = new Symbol<>("friend", 2, Boolean.class, false);
84 var friendView = new KeyOnlyRelationView<>(friend); 86 var friendView = new KeyOnlyRelationView<>(friend);
85 var trueDnf = Dnf.builder().parameter(p).clause().build(); 87 var trueDnf = Dnf.builder().parameter(p).clause().build();
@@ -95,8 +97,8 @@ class DnfBuilderTest {
95 97
96 @Test 98 @Test
97 void eliminateFalseDnfTest() { 99 void eliminateFalseDnfTest() {
98 var p = new Variable("p"); 100 var p = Variable.of("p");
99 var q = new Variable("q"); 101 var q = Variable.of("q");
100 var friend = new Symbol<>("friend", 2, Boolean.class, false); 102 var friend = new Symbol<>("friend", 2, Boolean.class, false);
101 var friendView = new KeyOnlyRelationView<>(friend); 103 var friendView = new KeyOnlyRelationView<>(friend);
102 var falseDnf = Dnf.builder().parameter(p).build(); 104 var falseDnf = Dnf.builder().parameter(p).build();
@@ -113,8 +115,8 @@ class DnfBuilderTest {
113 115
114 @Test 116 @Test
115 void alwaysTrueDnfTest() { 117 void alwaysTrueDnfTest() {
116 var p = new Variable("p"); 118 var p = Variable.of("p");
117 var q = new Variable("q"); 119 var q = Variable.of("q");
118 var friend = new Symbol<>("friend", 2, Boolean.class, false); 120 var friend = new Symbol<>("friend", 2, Boolean.class, false);
119 var friendView = new KeyOnlyRelationView<>(friend); 121 var friendView = new KeyOnlyRelationView<>(friend);
120 var trueDnf = Dnf.builder().parameter(p).clause().build(); 122 var trueDnf = Dnf.builder().parameter(p).clause().build();
@@ -131,8 +133,8 @@ class DnfBuilderTest {
131 133
132 @Test 134 @Test
133 void alwaysFalseDnfTest() { 135 void alwaysFalseDnfTest() {
134 var p = new Variable("p"); 136 var p = Variable.of("p");
135 var q = new Variable("q"); 137 var q = Variable.of("q");
136 var friend = new Symbol<>("friend", 2, Boolean.class, false); 138 var friend = new Symbol<>("friend", 2, Boolean.class, false);
137 var friendView = new KeyOnlyRelationView<>(friend); 139 var friendView = new KeyOnlyRelationView<>(friend);
138 var falseDnf = Dnf.builder().parameter(p).build(); 140 var falseDnf = Dnf.builder().parameter(p).build();
@@ -148,8 +150,8 @@ class DnfBuilderTest {
148 150
149 @Test 151 @Test
150 void eliminateNotFalseDnfTest() { 152 void eliminateNotFalseDnfTest() {
151 var p = new Variable("p"); 153 var p = Variable.of("p");
152 var q = new Variable("q"); 154 var q = Variable.of("q");
153 var friend = new Symbol<>("friend", 2, Boolean.class, false); 155 var friend = new Symbol<>("friend", 2, Boolean.class, false);
154 var friendView = new KeyOnlyRelationView<>(friend); 156 var friendView = new KeyOnlyRelationView<>(friend);
155 var falseDnf = Dnf.builder().parameter(p).build(); 157 var falseDnf = Dnf.builder().parameter(p).build();
@@ -165,8 +167,8 @@ class DnfBuilderTest {
165 167
166 @Test 168 @Test
167 void eliminateNotTrueDnfTest() { 169 void eliminateNotTrueDnfTest() {
168 var p = new Variable("p"); 170 var p = Variable.of("p");
169 var q = new Variable("q"); 171 var q = Variable.of("q");
170 var friend = new Symbol<>("friend", 2, Boolean.class, false); 172 var friend = new Symbol<>("friend", 2, Boolean.class, false);
171 var friendView = new KeyOnlyRelationView<>(friend); 173 var friendView = new KeyOnlyRelationView<>(friend);
172 var trueDnf = Dnf.builder().parameter(p).clause().build(); 174 var trueDnf = Dnf.builder().parameter(p).clause().build();
@@ -183,8 +185,8 @@ class DnfBuilderTest {
183 185
184 @Test 186 @Test
185 void alwaysNotFalseDnfTest() { 187 void alwaysNotFalseDnfTest() {
186 var p = new Variable("p"); 188 var p = Variable.of("p");
187 var q = new Variable("q"); 189 var q = Variable.of("q");
188 var friend = new Symbol<>("friend", 2, Boolean.class, false); 190 var friend = new Symbol<>("friend", 2, Boolean.class, false);
189 var friendView = new KeyOnlyRelationView<>(friend); 191 var friendView = new KeyOnlyRelationView<>(friend);
190 var falseDnf = Dnf.builder().parameter(p).build(); 192 var falseDnf = Dnf.builder().parameter(p).build();
@@ -201,8 +203,8 @@ class DnfBuilderTest {
201 203
202 @Test 204 @Test
203 void alwaysNotTrueDnfTest() { 205 void alwaysNotTrueDnfTest() {
204 var p = new Variable("p"); 206 var p = Variable.of("p");
205 var q = new Variable("q"); 207 var q = Variable.of("q");
206 var friend = new Symbol<>("friend", 2, Boolean.class, false); 208 var friend = new Symbol<>("friend", 2, Boolean.class, false);
207 var friendView = new KeyOnlyRelationView<>(friend); 209 var friendView = new KeyOnlyRelationView<>(friend);
208 var trueDnf = Dnf.builder().parameter(p).clause().build(); 210 var trueDnf = Dnf.builder().parameter(p).clause().build();
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/DnfToStringTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/DnfToDefinitionStringTest.java
index e6e4bef3..9b469bb0 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/DnfToStringTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/DnfToDefinitionStringTest.java
@@ -1,6 +1,8 @@
1package tools.refinery.store.query; 1package tools.refinery.store.query;
2 2
3import org.junit.jupiter.api.Test; 3import org.junit.jupiter.api.Test;
4import tools.refinery.store.query.dnf.Dnf;
5import tools.refinery.store.query.term.Variable;
4import tools.refinery.store.query.view.KeyOnlyRelationView; 6import tools.refinery.store.query.view.KeyOnlyRelationView;
5import tools.refinery.store.representation.Symbol; 7import tools.refinery.store.representation.Symbol;
6 8
@@ -8,13 +10,13 @@ import static org.hamcrest.MatcherAssert.assertThat;
8import static org.hamcrest.Matchers.is; 10import static org.hamcrest.Matchers.is;
9import static tools.refinery.store.query.literal.Literals.not; 11import static tools.refinery.store.query.literal.Literals.not;
10 12
11class DnfToStringTest { 13class DnfToDefinitionStringTest {
12 @Test 14 @Test
13 void noClausesTest() { 15 void noClausesTest() {
14 var p = new Variable("p"); 16 var p = Variable.of("p");
15 var dnf = Dnf.builder("Example").parameter(p).build(); 17 var dnf = Dnf.builder("Example").parameter(p).build();
16 18
17 assertThat(dnf.toString(), is(""" 19 assertThat(dnf.toDefinitionString(), is("""
18 pred Example(p) <-> 20 pred Example(p) <->
19 <no clauses>. 21 <no clauses>.
20 """)); 22 """));
@@ -24,7 +26,7 @@ class DnfToStringTest {
24 void noParametersTest() { 26 void noParametersTest() {
25 var dnf = Dnf.builder("Example").build(); 27 var dnf = Dnf.builder("Example").build();
26 28
27 assertThat(dnf.toString(), is(""" 29 assertThat(dnf.toDefinitionString(), is("""
28 pred Example() <-> 30 pred Example() <->
29 <no clauses>. 31 <no clauses>.
30 """)); 32 """));
@@ -32,10 +34,10 @@ class DnfToStringTest {
32 34
33 @Test 35 @Test
34 void emptyClauseTest() { 36 void emptyClauseTest() {
35 var p = new Variable("p"); 37 var p = Variable.of("p");
36 var dnf = Dnf.builder("Example").parameter(p).clause().build(); 38 var dnf = Dnf.builder("Example").parameter(p).clause().build();
37 39
38 assertThat(dnf.toString(), is(""" 40 assertThat(dnf.toDefinitionString(), is("""
39 pred Example(p) <-> 41 pred Example(p) <->
40 <empty>. 42 <empty>.
41 """)); 43 """));
@@ -43,13 +45,13 @@ class DnfToStringTest {
43 45
44 @Test 46 @Test
45 void relationViewPositiveTest() { 47 void relationViewPositiveTest() {
46 var p = new Variable("p"); 48 var p = Variable.of("p");
47 var q = new Variable("q"); 49 var q = Variable.of("q");
48 var friend = new Symbol<>("friend", 2, Boolean.class, false); 50 var friend = new Symbol<>("friend", 2, Boolean.class, false);
49 var friendView = new KeyOnlyRelationView<>(friend); 51 var friendView = new KeyOnlyRelationView<>(friend);
50 var dnf = Dnf.builder("Example").parameter(p).clause(friendView.call(p, q)).build(); 52 var dnf = Dnf.builder("Example").parameter(p).clause(friendView.call(p, q)).build();
51 53
52 assertThat(dnf.toString(), is(""" 54 assertThat(dnf.toDefinitionString(), is("""
53 pred Example(p) <-> 55 pred Example(p) <->
54 @RelationView("key") friend(p, q). 56 @RelationView("key") friend(p, q).
55 """)); 57 """));
@@ -57,13 +59,13 @@ class DnfToStringTest {
57 59
58 @Test 60 @Test
59 void relationViewNegativeTest() { 61 void relationViewNegativeTest() {
60 var p = new Variable("p"); 62 var p = Variable.of("p");
61 var q = new Variable("q"); 63 var q = Variable.of("q");
62 var friend = new Symbol<>("friend", 2, Boolean.class, false); 64 var friend = new Symbol<>("friend", 2, Boolean.class, false);
63 var friendView = new KeyOnlyRelationView<>(friend); 65 var friendView = new KeyOnlyRelationView<>(friend);
64 var dnf = Dnf.builder("Example").parameter(p).clause(not(friendView.call(p, q))).build(); 66 var dnf = Dnf.builder("Example").parameter(p).clause(not(friendView.call(p, q))).build();
65 67
66 assertThat(dnf.toString(), is(""" 68 assertThat(dnf.toDefinitionString(), is("""
67 pred Example(p) <-> 69 pred Example(p) <->
68 !(@RelationView("key") friend(p, q)). 70 !(@RelationView("key") friend(p, q)).
69 """)); 71 """));
@@ -71,13 +73,13 @@ class DnfToStringTest {
71 73
72 @Test 74 @Test
73 void relationViewTransitiveTest() { 75 void relationViewTransitiveTest() {
74 var p = new Variable("p"); 76 var p = Variable.of("p");
75 var q = new Variable("q"); 77 var q = Variable.of("q");
76 var friend = new Symbol<>("friend", 2, Boolean.class, false); 78 var friend = new Symbol<>("friend", 2, Boolean.class, false);
77 var friendView = new KeyOnlyRelationView<>(friend); 79 var friendView = new KeyOnlyRelationView<>(friend);
78 var dnf = Dnf.builder("Example").parameter(p).clause(friendView.callTransitive(p, q)).build(); 80 var dnf = Dnf.builder("Example").parameter(p).clause(friendView.callTransitive(p, q)).build();
79 81
80 assertThat(dnf.toString(), is(""" 82 assertThat(dnf.toDefinitionString(), is("""
81 pred Example(p) <-> 83 pred Example(p) <->
82 @RelationView("key") friend+(p, q). 84 @RelationView("key") friend+(p, q).
83 """)); 85 """));
@@ -85,13 +87,13 @@ class DnfToStringTest {
85 87
86 @Test 88 @Test
87 void multipleParametersTest() { 89 void multipleParametersTest() {
88 var p = new Variable("p"); 90 var p = Variable.of("p");
89 var q = new Variable("q"); 91 var q = Variable.of("q");
90 var friend = new Symbol<>("friend", 2, Boolean.class, false); 92 var friend = new Symbol<>("friend", 2, Boolean.class, false);
91 var friendView = new KeyOnlyRelationView<>(friend); 93 var friendView = new KeyOnlyRelationView<>(friend);
92 var dnf = Dnf.builder("Example").parameters(p, q).clause(friendView.call(p, q)).build(); 94 var dnf = Dnf.builder("Example").parameters(p, q).clause(friendView.call(p, q)).build();
93 95
94 assertThat(dnf.toString(), is(""" 96 assertThat(dnf.toDefinitionString(), is("""
95 pred Example(p, q) <-> 97 pred Example(p, q) <->
96 @RelationView("key") friend(p, q). 98 @RelationView("key") friend(p, q).
97 """)); 99 """));
@@ -99,8 +101,8 @@ class DnfToStringTest {
99 101
100 @Test 102 @Test
101 void multipleLiteralsTest() { 103 void multipleLiteralsTest() {
102 var p = new Variable("p"); 104 var p = Variable.of("p");
103 var q = new Variable("q"); 105 var q = Variable.of("q");
104 var person = new Symbol<>("person", 1, Boolean.class, false); 106 var person = new Symbol<>("person", 1, Boolean.class, false);
105 var personView = new KeyOnlyRelationView<>(person); 107 var personView = new KeyOnlyRelationView<>(person);
106 var friend = new Symbol<>("friend", 2, Boolean.class, false); 108 var friend = new Symbol<>("friend", 2, Boolean.class, false);
@@ -114,7 +116,7 @@ class DnfToStringTest {
114 ) 116 )
115 .build(); 117 .build();
116 118
117 assertThat(dnf.toString(), is(""" 119 assertThat(dnf.toDefinitionString(), is("""
118 pred Example(p) <-> 120 pred Example(p) <->
119 @RelationView("key") person(p), 121 @RelationView("key") person(p),
120 @RelationView("key") person(q), 122 @RelationView("key") person(q),
@@ -124,8 +126,8 @@ class DnfToStringTest {
124 126
125 @Test 127 @Test
126 void multipleClausesTest() { 128 void multipleClausesTest() {
127 var p = new Variable("p"); 129 var p = Variable.of("p");
128 var q = new Variable("q"); 130 var q = Variable.of("q");
129 var friend = new Symbol<>("friend", 2, Boolean.class, false); 131 var friend = new Symbol<>("friend", 2, Boolean.class, false);
130 var friendView = new KeyOnlyRelationView<>(friend); 132 var friendView = new KeyOnlyRelationView<>(friend);
131 var dnf = Dnf.builder("Example") 133 var dnf = Dnf.builder("Example")
@@ -134,7 +136,7 @@ class DnfToStringTest {
134 .clause(friendView.call(q, p)) 136 .clause(friendView.call(q, p))
135 .build(); 137 .build();
136 138
137 assertThat(dnf.toString(), is(""" 139 assertThat(dnf.toDefinitionString(), is("""
138 pred Example(p) <-> 140 pred Example(p) <->
139 @RelationView("key") friend(p, q) 141 @RelationView("key") friend(p, q)
140 ; 142 ;
@@ -144,10 +146,10 @@ class DnfToStringTest {
144 146
145 @Test 147 @Test
146 void dnfTest() { 148 void dnfTest() {
147 var p = new Variable("p"); 149 var p = Variable.of("p");
148 var q = new Variable("q"); 150 var q = Variable.of("q");
149 var r = new Variable("r"); 151 var r = Variable.of("r");
150 var s = new Variable("s"); 152 var s = Variable.of("s");
151 var person = new Symbol<>("person", 1, Boolean.class, false); 153 var person = new Symbol<>("person", 1, Boolean.class, false);
152 var personView = new KeyOnlyRelationView<>(person); 154 var personView = new KeyOnlyRelationView<>(person);
153 var friend = new Symbol<>("friend", 2, Boolean.class, false); 155 var friend = new Symbol<>("friend", 2, Boolean.class, false);
@@ -162,7 +164,7 @@ class DnfToStringTest {
162 ) 164 )
163 .build(); 165 .build();
164 166
165 assertThat(dnf.toString(), is(""" 167 assertThat(dnf.toDefinitionString(), is("""
166 pred Example(p) <-> 168 pred Example(p) <->
167 @RelationView("key") person(p), 169 @RelationView("key") person(p),
168 @RelationView("key") person(q), 170 @RelationView("key") person(q),
diff --git a/subprojects/store-query/src/test/java/tools/refinery/store/query/tests/StructurallyEqualToTest.java b/subprojects/store-query/src/test/java/tools/refinery/store/query/tests/StructurallyEqualToTest.java
index 0cda22df..a61e2b65 100644
--- a/subprojects/store-query/src/test/java/tools/refinery/store/query/tests/StructurallyEqualToTest.java
+++ b/subprojects/store-query/src/test/java/tools/refinery/store/query/tests/StructurallyEqualToTest.java
@@ -1,8 +1,8 @@
1package tools.refinery.store.query.tests; 1package tools.refinery.store.query.tests;
2 2
3import org.junit.jupiter.api.Test; 3import org.junit.jupiter.api.Test;
4import tools.refinery.store.query.Dnf; 4import tools.refinery.store.query.dnf.Dnf;
5import tools.refinery.store.query.Variable; 5import tools.refinery.store.query.term.Variable;
6import tools.refinery.store.query.view.KeyOnlyRelationView; 6import tools.refinery.store.query.view.KeyOnlyRelationView;
7import tools.refinery.store.representation.Symbol; 7import tools.refinery.store.representation.Symbol;
8 8
@@ -14,8 +14,8 @@ import static tools.refinery.store.query.tests.QueryMatchers.structurallyEqualTo
14class StructurallyEqualToTest { 14class StructurallyEqualToTest {
15 @Test 15 @Test
16 void flatEqualsTest() { 16 void flatEqualsTest() {
17 var p = new Variable("p"); 17 var p = Variable.of("p");
18 var q = new Variable("q"); 18 var q = Variable.of("q");
19 var person = new Symbol<>("Person", 1, Boolean.class, false); 19 var person = new Symbol<>("Person", 1, Boolean.class, false);
20 var personView = new KeyOnlyRelationView<>(person); 20 var personView = new KeyOnlyRelationView<>(person);
21 21
@@ -27,8 +27,8 @@ class StructurallyEqualToTest {
27 27
28 @Test 28 @Test
29 void flatNotEqualsTest() { 29 void flatNotEqualsTest() {
30 var p = new Variable("p"); 30 var p = Variable.of("p");
31 var q = new Variable("q"); 31 var q = Variable.of("q");
32 var person = new Symbol<>("Person", 1, Boolean.class, false); 32 var person = new Symbol<>("Person", 1, Boolean.class, false);
33 var personView = new KeyOnlyRelationView<>(person); 33 var personView = new KeyOnlyRelationView<>(person);
34 34
@@ -41,8 +41,8 @@ class StructurallyEqualToTest {
41 41
42 @Test 42 @Test
43 void deepEqualsTest() { 43 void deepEqualsTest() {
44 var p = new Variable("p"); 44 var p = Variable.of("p");
45 var q = new Variable("q"); 45 var q = Variable.of("q");
46 var person = new Symbol<>("Person", 1, Boolean.class, false); 46 var person = new Symbol<>("Person", 1, Boolean.class, false);
47 var personView = new KeyOnlyRelationView<>(person); 47 var personView = new KeyOnlyRelationView<>(person);
48 48
@@ -58,8 +58,8 @@ class StructurallyEqualToTest {
58 58
59 @Test 59 @Test
60 void deepNotEqualsTest() { 60 void deepNotEqualsTest() {
61 var p = new Variable("p"); 61 var p = Variable.of("p");
62 var q = new Variable("q"); 62 var q = Variable.of("q");
63 var person = new Symbol<>("Person", 1, Boolean.class, false); 63 var person = new Symbol<>("Person", 1, Boolean.class, false);
64 var personView = new KeyOnlyRelationView<>(person); 64 var personView = new KeyOnlyRelationView<>(person);
65 65
@@ -72,6 +72,6 @@ class StructurallyEqualToTest {
72 72
73 var assertion = structurallyEqualTo(expected); 73 var assertion = structurallyEqualTo(expected);
74 var error = assertThrows(AssertionError.class, () -> assertThat(actual, assertion)); 74 var error = assertThrows(AssertionError.class, () -> assertThat(actual, assertion));
75 assertThat(error.getMessage(), containsString(" called from Expected ")); 75 assertThat(error.getMessage(), containsString(" called from Expected/1 "));
76 } 76 }
77} 77}
diff --git a/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/MismatchDescribingDnfEqualityChecker.java b/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/MismatchDescribingDnfEqualityChecker.java
index aaab2e7e..685957c9 100644
--- a/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/MismatchDescribingDnfEqualityChecker.java
+++ b/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/MismatchDescribingDnfEqualityChecker.java
@@ -30,14 +30,14 @@ class MismatchDescribingDnfEqualityChecker extends DeepDnfEqualityChecker {
30 var inProgress = getInProgress(); 30 var inProgress = getInProgress();
31 int size = inProgress.size(); 31 int size = inProgress.size();
32 if (size <= 1) { 32 if (size <= 1) {
33 description.appendText("was ").appendValue(pair.left()); 33 description.appendText("was ").appendText(pair.left().toDefinitionString());
34 return; 34 return;
35 } 35 }
36 var last = inProgress.get(size - 1); 36 var last = inProgress.get(size - 1);
37 description.appendText("expected ").appendValue(last.right()); 37 description.appendText("expected ").appendText(last.right().toDefinitionString());
38 for (int i = size - 2; i >= 0; i--) { 38 for (int i = size - 2; i >= 0; i--) {
39 description.appendText(" called from ").appendText(inProgress.get(i).left().name()); 39 description.appendText(" called from ").appendText(inProgress.get(i).left().toString());
40 } 40 }
41 description.appendText(" was not structurally equal to ").appendValue(last.right()); 41 description.appendText(" was not structurally equal to ").appendText(last.right().toDefinitionString());
42 } 42 }
43} 43}
diff --git a/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/QueryMatchers.java b/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/QueryMatchers.java
index 83614278..bf1c1b74 100644
--- a/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/QueryMatchers.java
+++ b/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/QueryMatchers.java
@@ -1,7 +1,7 @@
1package tools.refinery.store.query.tests; 1package tools.refinery.store.query.tests;
2 2
3import org.hamcrest.Matcher; 3import org.hamcrest.Matcher;
4import tools.refinery.store.query.Dnf; 4import tools.refinery.store.query.dnf.Dnf;
5 5
6public final class QueryMatchers { 6public final class QueryMatchers {
7 private QueryMatchers() { 7 private QueryMatchers() {
diff --git a/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/StructurallyEqualTo.java b/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/StructurallyEqualTo.java
index a42396dd..a9a78f88 100644
--- a/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/StructurallyEqualTo.java
+++ b/subprojects/store-query/src/testFixtures/java/tools/refinery/store/query/tests/StructurallyEqualTo.java
@@ -2,7 +2,7 @@ package tools.refinery.store.query.tests;
2 2
3import org.hamcrest.Description; 3import org.hamcrest.Description;
4import org.hamcrest.TypeSafeMatcher; 4import org.hamcrest.TypeSafeMatcher;
5import tools.refinery.store.query.Dnf; 5import tools.refinery.store.query.dnf.Dnf;
6import tools.refinery.store.query.equality.DeepDnfEqualityChecker; 6import tools.refinery.store.query.equality.DeepDnfEqualityChecker;
7 7
8public class StructurallyEqualTo extends TypeSafeMatcher<Dnf> { 8public class StructurallyEqualTo extends TypeSafeMatcher<Dnf> {
@@ -31,6 +31,6 @@ public class StructurallyEqualTo extends TypeSafeMatcher<Dnf> {
31 31
32 @Override 32 @Override
33 public void describeTo(Description description) { 33 public void describeTo(Description description) {
34 description.appendText("structurally equal to ").appendValue(expected); 34 description.appendText("structurally equal to ").appendText(expected.toDefinitionString());
35 } 35 }
36} 36}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/PartialInterpretation.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/PartialInterpretation.java
index 99656da8..4f195e97 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/PartialInterpretation.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/PartialInterpretation.java
@@ -12,8 +12,6 @@ public non-sealed interface PartialInterpretation<A, C> extends AnyPartialInterp
12 12
13 Cursor<Tuple, A> getAll(); 13 Cursor<Tuple, A> getAll();
14 14
15 Cursor<Tuple, A> getAllErrors();
16
17 MergeResult merge(Tuple key, A value); 15 MergeResult merge(Tuple key, A value);
18 16
19 C getConcrete(Tuple key); 17 C getConcrete(Tuple key);
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java
index e602242e..de039dd9 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningAdapter.java
@@ -3,7 +3,7 @@ package tools.refinery.store.reasoning;
3import tools.refinery.store.adapter.ModelAdapter; 3import tools.refinery.store.adapter.ModelAdapter;
4import tools.refinery.store.reasoning.representation.AnyPartialSymbol; 4import tools.refinery.store.reasoning.representation.AnyPartialSymbol;
5import tools.refinery.store.reasoning.representation.PartialSymbol; 5import tools.refinery.store.reasoning.representation.PartialSymbol;
6import tools.refinery.store.query.Dnf; 6import tools.refinery.store.query.dnf.Dnf;
7import tools.refinery.store.query.ResultSet; 7import tools.refinery.store.query.ResultSet;
8 8
9public interface ReasoningAdapter extends ModelAdapter { 9public interface ReasoningAdapter extends ModelAdapter {
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningBuilder.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningBuilder.java
index 588c2711..4030d296 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningBuilder.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningBuilder.java
@@ -3,7 +3,7 @@ package tools.refinery.store.reasoning;
3import tools.refinery.store.adapter.ModelAdapterBuilder; 3import tools.refinery.store.adapter.ModelAdapterBuilder;
4import tools.refinery.store.model.ModelStore; 4import tools.refinery.store.model.ModelStore;
5import tools.refinery.store.reasoning.literal.Modality; 5import tools.refinery.store.reasoning.literal.Modality;
6import tools.refinery.store.query.Dnf; 6import tools.refinery.store.query.dnf.Dnf;
7 7
8import java.util.Collection; 8import java.util.Collection;
9import java.util.List; 9import java.util.List;
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningStoreAdapter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningStoreAdapter.java
index 69c0f5eb..f6a6e414 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningStoreAdapter.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/ReasoningStoreAdapter.java
@@ -3,7 +3,7 @@ package tools.refinery.store.reasoning;
3import tools.refinery.store.adapter.ModelStoreAdapter; 3import tools.refinery.store.adapter.ModelStoreAdapter;
4import tools.refinery.store.model.Model; 4import tools.refinery.store.model.Model;
5import tools.refinery.store.reasoning.representation.AnyPartialSymbol; 5import tools.refinery.store.reasoning.representation.AnyPartialSymbol;
6import tools.refinery.store.query.Dnf; 6import tools.refinery.store.query.dnf.Dnf;
7 7
8import java.util.Collection; 8import java.util.Collection;
9 9
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 a7a56680..0acf0d49 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
@@ -4,7 +4,7 @@ import tools.refinery.store.model.Model;
4import tools.refinery.store.reasoning.ReasoningAdapter; 4import tools.refinery.store.reasoning.ReasoningAdapter;
5import tools.refinery.store.reasoning.PartialInterpretation; 5import tools.refinery.store.reasoning.PartialInterpretation;
6import tools.refinery.store.reasoning.representation.PartialSymbol; 6import tools.refinery.store.reasoning.representation.PartialSymbol;
7import tools.refinery.store.query.Dnf; 7import tools.refinery.store.query.dnf.Dnf;
8import tools.refinery.store.query.ResultSet; 8import tools.refinery.store.query.ResultSet;
9 9
10public class ReasoningAdapterImpl implements ReasoningAdapter { 10public class ReasoningAdapterImpl implements ReasoningAdapter {
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java
index 2860e2b9..e11b14bf 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/internal/ReasoningBuilderImpl.java
@@ -5,7 +5,7 @@ import tools.refinery.store.model.ModelStore;
5import tools.refinery.store.model.ModelStoreBuilder; 5import tools.refinery.store.model.ModelStoreBuilder;
6import tools.refinery.store.reasoning.ReasoningBuilder; 6import tools.refinery.store.reasoning.ReasoningBuilder;
7import tools.refinery.store.reasoning.literal.Modality; 7import tools.refinery.store.reasoning.literal.Modality;
8import tools.refinery.store.query.Dnf; 8import tools.refinery.store.query.dnf.Dnf;
9 9
10public class ReasoningBuilderImpl extends AbstractModelAdapterBuilder implements ReasoningBuilder { 10public class ReasoningBuilderImpl extends AbstractModelAdapterBuilder implements ReasoningBuilder {
11 public ReasoningBuilderImpl(ModelStoreBuilder storeBuilder) { 11 public ReasoningBuilderImpl(ModelStoreBuilder storeBuilder) {
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 763dad6d..ac06e68b 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
@@ -4,7 +4,7 @@ import tools.refinery.store.reasoning.ReasoningStoreAdapter;
4import tools.refinery.store.model.Model; 4import tools.refinery.store.model.Model;
5import tools.refinery.store.model.ModelStore; 5import tools.refinery.store.model.ModelStore;
6import tools.refinery.store.reasoning.representation.AnyPartialSymbol; 6import tools.refinery.store.reasoning.representation.AnyPartialSymbol;
7import tools.refinery.store.query.Dnf; 7import tools.refinery.store.query.dnf.Dnf;
8 8
9import java.util.Collection; 9import java.util.Collection;
10 10
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/DnfLifter.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/DnfLifter.java
index 966e080a..2b0e0f08 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/DnfLifter.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/DnfLifter.java
@@ -1,18 +1,18 @@
1package tools.refinery.store.reasoning.lifting; 1package tools.refinery.store.reasoning.lifting;
2 2
3import org.jetbrains.annotations.Nullable; 3import org.jetbrains.annotations.Nullable;
4import tools.refinery.store.reasoning.literal.ModalDnfCallLiteral; 4import tools.refinery.store.query.dnf.Dnf;
5import tools.refinery.store.reasoning.Reasoning; 5import tools.refinery.store.query.dnf.DnfBuilder;
6import tools.refinery.store.reasoning.literal.ModalRelationLiteral; 6import tools.refinery.store.query.dnf.DnfClause;
7import tools.refinery.store.reasoning.literal.PartialRelationLiteral; 7import tools.refinery.store.query.literal.CallLiteral;
8import tools.refinery.store.query.Dnf;
9import tools.refinery.store.query.DnfBuilder;
10import tools.refinery.store.query.DnfClause;
11import tools.refinery.store.query.Variable;
12import tools.refinery.store.query.literal.CallPolarity; 8import tools.refinery.store.query.literal.CallPolarity;
13import tools.refinery.store.query.literal.DnfCallLiteral;
14import tools.refinery.store.query.literal.Literal; 9import tools.refinery.store.query.literal.Literal;
10import tools.refinery.store.query.term.DataVariable;
11import tools.refinery.store.query.term.Variable;
12import tools.refinery.store.reasoning.Reasoning;
13import tools.refinery.store.reasoning.literal.ModalConstraint;
15import tools.refinery.store.reasoning.literal.Modality; 14import tools.refinery.store.reasoning.literal.Modality;
15import tools.refinery.store.reasoning.literal.PartialLiterals;
16import tools.refinery.store.util.CycleDetectingMapper; 16import tools.refinery.store.util.CycleDetectingMapper;
17 17
18import java.util.ArrayList; 18import java.util.ArrayList;
@@ -46,7 +46,10 @@ public class DnfLifter {
46 46
47 private boolean liftClause(Modality modality, DnfClause clause, DnfBuilder builder) { 47 private boolean liftClause(Modality modality, DnfClause clause, DnfBuilder builder) {
48 boolean changed = false; 48 boolean changed = false;
49 var quantifiedVariables = new HashSet<>(clause.quantifiedVariables()); 49 var quantifiedVariables = new HashSet<>(clause.boundVariables()
50 .stream()
51 .filter(DataVariable.class::isInstance)
52 .toList());
50 var literals = clause.literals(); 53 var literals = clause.literals();
51 var liftedLiterals = new ArrayList<Literal>(literals.size()); 54 var liftedLiterals = new ArrayList<Literal>(literals.size());
52 for (var literal : literals) { 55 for (var literal : literals) {
@@ -65,8 +68,8 @@ public class DnfLifter {
65 } 68 }
66 } 69 }
67 for (var quantifiedVariable : quantifiedVariables) { 70 for (var quantifiedVariable : quantifiedVariables) {
68 // Quantify over variables that are not already quantified with the expected modality. 71 // Quantify over data variables that are not already quantified with the expected modality.
69 liftedLiterals.add(Reasoning.EXISTS.call(CallPolarity.POSITIVE, modality, 72 liftedLiterals.add(new CallLiteral(CallPolarity.POSITIVE, new ModalConstraint(modality, Reasoning.EXISTS),
70 List.of(quantifiedVariable))); 73 List.of(quantifiedVariable)));
71 } 74 }
72 builder.clause(liftedLiterals); 75 builder.clause(liftedLiterals);
@@ -75,33 +78,39 @@ public class DnfLifter {
75 78
76 @Nullable 79 @Nullable
77 private Variable isExistsLiteralForVariable(Modality modality, Literal literal) { 80 private Variable isExistsLiteralForVariable(Modality modality, Literal literal) {
78 if (literal instanceof ModalRelationLiteral modalRelationLiteral && 81 if (literal instanceof CallLiteral callLiteral &&
79 modalRelationLiteral.getPolarity() == CallPolarity.POSITIVE && 82 callLiteral.getPolarity() == CallPolarity.POSITIVE &&
80 modalRelationLiteral.getModality() == modality && 83 callLiteral.getTarget() instanceof ModalConstraint modalConstraint &&
81 modalRelationLiteral.getTarget().equals(Reasoning.EXISTS)) { 84 modalConstraint.modality() == modality &&
82 return modalRelationLiteral.getArguments().get(0); 85 modalConstraint.constraint().equals(Reasoning.EXISTS)) {
86 return callLiteral.getArguments().get(0);
83 } 87 }
84 return null; 88 return null;
85 } 89 }
86 90
87 @Nullable 91 @Nullable
88 private Literal liftLiteral(Modality modality, Literal literal) { 92 private Literal liftLiteral(Modality modality, Literal literal) {
89 if (literal instanceof PartialRelationLiteral partialRelationLiteral) { 93 if (!(literal instanceof CallLiteral callLiteral)) {
90 return new ModalRelationLiteral(modality, partialRelationLiteral); 94 return null;
91 } else if (literal instanceof DnfCallLiteral dnfCallLiteral) { 95 }
92 var polarity = dnfCallLiteral.getPolarity(); 96 var target = callLiteral.getTarget();
93 var target = dnfCallLiteral.getTarget(); 97 if (target instanceof ModalConstraint modalTarget) {
94 var liftedTarget = lift(modality.commute(polarity), target); 98 var actualTarget = modalTarget.constraint();
95 if (target.equals(liftedTarget)) { 99 if (actualTarget instanceof Dnf dnf) {
96 return null; 100 var targetModality = modalTarget.modality();
101 var liftedTarget = lift(targetModality, dnf);
102 return new CallLiteral(callLiteral.getPolarity(), liftedTarget, callLiteral.getArguments());
97 } 103 }
98 return new DnfCallLiteral(polarity, liftedTarget, dnfCallLiteral.getArguments()); 104 // No more lifting to be done, pass any modal call to a partial symbol through.
99 } else if (literal instanceof ModalDnfCallLiteral modalDnfCallLiteral) {
100 var liftedTarget = lift(modalDnfCallLiteral.getModality(), modalDnfCallLiteral.getTarget());
101 return new DnfCallLiteral(modalDnfCallLiteral.getPolarity(), liftedTarget,
102 modalDnfCallLiteral.getArguments());
103 } else {
104 return null; 105 return null;
106 } else if (target instanceof Dnf dnf) {
107 var polarity = callLiteral.getPolarity();
108 var liftedTarget = lift(modality.commute(polarity), dnf);
109 // Use == instead of equals(), because lift will return the same object by reference is there are no
110 // changes made during lifting.
111 return liftedTarget == target ? null : new CallLiteral(polarity, liftedTarget, callLiteral.getArguments());
112 } else {
113 return PartialLiterals.addModality(callLiteral, modality);
105 } 114 }
106 } 115 }
107} 116}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ModalDnf.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ModalDnf.java
index 7aa98bf2..ec381bb8 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ModalDnf.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/lifting/ModalDnf.java
@@ -1,9 +1,9 @@
1package tools.refinery.store.reasoning.lifting; 1package tools.refinery.store.reasoning.lifting;
2 2
3import tools.refinery.store.query.Dnf; 3import tools.refinery.store.query.dnf.Dnf;
4import tools.refinery.store.reasoning.literal.Modality; 4import tools.refinery.store.reasoning.literal.Modality;
5 5
6public record ModalDnf(Modality modality, Dnf dnf) { 6record ModalDnf(Modality modality, Dnf dnf) {
7 @Override 7 @Override
8 public String toString() { 8 public String toString() {
9 return "%s %s".formatted(modality, dnf.name()); 9 return "%s %s".formatted(modality, dnf.name());
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalConstraint.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalConstraint.java
new file mode 100644
index 00000000..2fbb4607
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalConstraint.java
@@ -0,0 +1,46 @@
1package tools.refinery.store.reasoning.literal;
2
3import tools.refinery.store.query.Constraint;
4import tools.refinery.store.query.equality.LiteralEqualityHelper;
5import tools.refinery.store.query.literal.LiteralReduction;
6import tools.refinery.store.query.term.Sort;
7
8import java.util.List;
9
10public record ModalConstraint(Modality modality, Constraint constraint) implements Constraint {
11 private static final String FORMAT = "%s %s";
12
13 @Override
14 public String name() {
15 return FORMAT.formatted(modality, constraint.name());
16 }
17
18 @Override
19 public List<Sort> getSorts() {
20 return constraint.getSorts();
21 }
22
23 @Override
24 public LiteralReduction getReduction() {
25 return constraint.getReduction();
26 }
27
28 @Override
29 public boolean equals(LiteralEqualityHelper helper, Constraint other) {
30 if (getClass() != other.getClass()) {
31 return false;
32 }
33 var otherModalConstraint = (ModalConstraint) other;
34 return modality == otherModalConstraint.modality && constraint.equals(helper, otherModalConstraint.constraint);
35 }
36
37 @Override
38 public String toReferenceString() {
39 return FORMAT.formatted(modality, constraint.toReferenceString());
40 }
41
42 @Override
43 public String toString() {
44 return FORMAT.formatted(modality, constraint);
45 }
46}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalDnfCallLiteral.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalDnfCallLiteral.java
deleted file mode 100644
index 1090f1ae..00000000
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalDnfCallLiteral.java
+++ /dev/null
@@ -1,48 +0,0 @@
1package tools.refinery.store.reasoning.literal;
2
3import tools.refinery.store.query.Dnf;
4import tools.refinery.store.query.Variable;
5import tools.refinery.store.query.equality.LiteralEqualityHelper;
6import tools.refinery.store.query.literal.CallPolarity;
7import tools.refinery.store.query.literal.DnfCallLiteral;
8import tools.refinery.store.query.literal.LiteralReduction;
9import tools.refinery.store.query.literal.PolarLiteral;
10import tools.refinery.store.query.substitution.Substitution;
11
12import java.util.List;
13
14public class ModalDnfCallLiteral extends ModalLiteral<Dnf> implements PolarLiteral<ModalDnfCallLiteral> {
15 public ModalDnfCallLiteral(CallPolarity polarity, Modality modality, Dnf target, List<Variable> arguments) {
16 super(polarity, modality, target, arguments);
17 }
18
19 public ModalDnfCallLiteral(Modality modality, DnfCallLiteral baseLiteral) {
20 super(modality.commute(baseLiteral.getPolarity()), baseLiteral);
21 }
22
23 @Override
24 public Class<Dnf> getTargetType() {
25 return Dnf.class;
26 }
27
28 @Override
29 protected boolean targetEquals(LiteralEqualityHelper helper, Dnf otherTarget) {
30 return helper.dnfEqual(getTarget(), otherTarget);
31 }
32
33 @Override
34 public ModalDnfCallLiteral substitute(Substitution substitution) {
35 return new ModalDnfCallLiteral(getPolarity(), getModality(), getTarget(), substituteArguments(substitution));
36 }
37
38 @Override
39 public ModalDnfCallLiteral negate() {
40 return new ModalDnfCallLiteral(getPolarity().negate(), getModality(), getTarget(), getArguments());
41 }
42
43 @Override
44 public LiteralReduction getReduction() {
45 var dnfReduction = getTarget().getReduction();
46 return getPolarity().isPositive() ? dnfReduction : dnfReduction.negate();
47 }
48}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalLiteral.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalLiteral.java
deleted file mode 100644
index 5992f172..00000000
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalLiteral.java
+++ /dev/null
@@ -1,63 +0,0 @@
1package tools.refinery.store.reasoning.literal;
2
3import tools.refinery.store.query.RelationLike;
4import tools.refinery.store.query.Variable;
5import tools.refinery.store.query.equality.LiteralEqualityHelper;
6import tools.refinery.store.query.literal.CallLiteral;
7import tools.refinery.store.query.literal.CallPolarity;
8import tools.refinery.store.query.literal.Literal;
9
10import java.util.List;
11import java.util.Objects;
12
13public abstract class ModalLiteral<T extends RelationLike> extends CallLiteral<T> {
14 private final Modality modality;
15
16 protected ModalLiteral(CallPolarity polarity, Modality modality, T target, List<Variable> arguments) {
17 super(polarity, target, arguments);
18 this.modality = modality;
19 }
20
21 protected ModalLiteral(Modality modality, CallLiteral<? extends T> baseLiteral) {
22 this(baseLiteral.getPolarity(), commute(modality, baseLiteral.getPolarity()), baseLiteral.getTarget(),
23 baseLiteral.getArguments());
24 }
25
26 public Modality getModality() {
27 return modality;
28 }
29
30 @Override
31 public boolean equalsWithSubstitution(LiteralEqualityHelper helper, Literal other) {
32 if (!super.equalsWithSubstitution(helper, other)) {
33 return false;
34 }
35 // If {@link CallLiteral#equalsWithSubstitution(LiteralEqualityHelper, Literal)} has returned {@code true},
36 // we must have the same dynamic type as {@code other}.
37 var otherModalLiteral = (ModalLiteral<?>) other;
38 return modality == otherModalLiteral.modality;
39 }
40
41 @Override
42 protected String targetToString() {
43 return "%s %s".formatted(modality, super.targetToString());
44 }
45
46 @Override
47 public boolean equals(Object o) {
48 if (this == o) return true;
49 if (o == null || getClass() != o.getClass()) return false;
50 if (!super.equals(o)) return false;
51 ModalLiteral<?> that = (ModalLiteral<?>) o;
52 return modality == that.modality;
53 }
54
55 @Override
56 public int hashCode() {
57 return Objects.hash(super.hashCode(), modality);
58 }
59
60 private static Modality commute(Modality modality, CallPolarity polarity) {
61 return polarity.isPositive() ? modality : modality.negate();
62 }
63}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalRelationLiteral.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalRelationLiteral.java
deleted file mode 100644
index 9c72bd37..00000000
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/ModalRelationLiteral.java
+++ /dev/null
@@ -1,37 +0,0 @@
1package tools.refinery.store.reasoning.literal;
2
3import tools.refinery.store.reasoning.representation.PartialRelation;
4import tools.refinery.store.query.Variable;
5import tools.refinery.store.query.literal.CallPolarity;
6import tools.refinery.store.query.literal.PolarLiteral;
7import tools.refinery.store.query.substitution.Substitution;
8
9import java.util.List;
10
11public final class ModalRelationLiteral extends ModalLiteral<PartialRelation>
12 implements PolarLiteral<ModalRelationLiteral> {
13 public ModalRelationLiteral(CallPolarity polarity, Modality modality, PartialRelation target,
14 List<Variable> arguments) {
15 super(polarity, modality, target, arguments);
16 }
17
18
19 public ModalRelationLiteral(Modality modality, PartialRelationLiteral baseLiteral) {
20 super(modality, baseLiteral);
21 }
22
23 @Override
24 public Class<PartialRelation> getTargetType() {
25 return PartialRelation.class;
26 }
27
28 @Override
29 public ModalRelationLiteral substitute(Substitution substitution) {
30 return new ModalRelationLiteral(getPolarity(), getModality(), getTarget(), substituteArguments(substitution));
31 }
32
33 @Override
34 public ModalRelationLiteral negate() {
35 return new ModalRelationLiteral(getPolarity().negate(), getModality(), getTarget(), getArguments());
36 }
37}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialLiterals.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialLiterals.java
index 10e4c7f7..f991f87f 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialLiterals.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialLiterals.java
@@ -1,33 +1,31 @@
1package tools.refinery.store.reasoning.literal; 1package tools.refinery.store.reasoning.literal;
2 2
3import tools.refinery.store.query.literal.DnfCallLiteral; 3import tools.refinery.store.query.literal.CallLiteral;
4 4
5public final class PartialLiterals { 5public final class PartialLiterals {
6 private PartialLiterals() { 6 private PartialLiterals() {
7 throw new IllegalStateException("This is a static utility class and should not be instantiated directly"); 7 throw new IllegalStateException("This is a static utility class and should not be instantiated directly");
8 } 8 }
9 9
10 public ModalRelationLiteral may(PartialRelationLiteral literal) { 10 public static CallLiteral may(CallLiteral literal) {
11 return new ModalRelationLiteral(Modality.MAY, literal); 11 return addModality(literal, Modality.MAY);
12 } 12 }
13 13
14 public ModalRelationLiteral must(PartialRelationLiteral literal) { 14 public static CallLiteral must(CallLiteral literal) {
15 return new ModalRelationLiteral(Modality.MUST, literal); 15 return addModality(literal, Modality.MUST);
16 } 16 }
17 17
18 public ModalRelationLiteral current(PartialRelationLiteral literal) { 18 public static CallLiteral current(CallLiteral literal) {
19 return new ModalRelationLiteral(Modality.CURRENT, literal); 19 return addModality(literal, Modality.CURRENT);
20 } 20 }
21 21
22 public ModalDnfCallLiteral may(DnfCallLiteral literal) { 22 public static CallLiteral addModality(CallLiteral literal, Modality modality) {
23 return new ModalDnfCallLiteral(Modality.MAY, literal); 23 var target = literal.getTarget();
24 } 24 if (target instanceof ModalConstraint) {
25 25 throw new IllegalArgumentException("Literal %s already has modality".formatted(literal));
26 public ModalDnfCallLiteral must(DnfCallLiteral literal) { 26 }
27 return new ModalDnfCallLiteral(Modality.MUST, literal); 27 var polarity = literal.getPolarity();
28 } 28 var modalTarget = new ModalConstraint(modality.commute(polarity), target);
29 29 return new CallLiteral(polarity, modalTarget, literal.getArguments());
30 public ModalDnfCallLiteral current(DnfCallLiteral literal) {
31 return new ModalDnfCallLiteral(Modality.CURRENT, literal);
32 } 30 }
33} 31}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialRelationLiteral.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialRelationLiteral.java
deleted file mode 100644
index aff84538..00000000
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/literal/PartialRelationLiteral.java
+++ /dev/null
@@ -1,32 +0,0 @@
1package tools.refinery.store.reasoning.literal;
2
3import tools.refinery.store.reasoning.representation.PartialRelation;
4import tools.refinery.store.query.Variable;
5import tools.refinery.store.query.literal.CallLiteral;
6import tools.refinery.store.query.literal.CallPolarity;
7import tools.refinery.store.query.literal.PolarLiteral;
8import tools.refinery.store.query.substitution.Substitution;
9
10import java.util.List;
11
12public final class PartialRelationLiteral extends CallLiteral<PartialRelation>
13 implements PolarLiteral<PartialRelationLiteral> {
14 public PartialRelationLiteral(CallPolarity polarity, PartialRelation target, List<Variable> substitution) {
15 super(polarity, target, substitution);
16 }
17
18 @Override
19 public Class<PartialRelation> getTargetType() {
20 return PartialRelation.class;
21 }
22
23 @Override
24 public PartialRelationLiteral substitute(Substitution substitution) {
25 return new PartialRelationLiteral(getPolarity(), getTarget(), substituteArguments(substitution));
26 }
27
28 @Override
29 public PartialRelationLiteral negate() {
30 return new PartialRelationLiteral(getPolarity().negate(), getTarget(), getArguments());
31 }
32}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/representation/PartialRelation.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/representation/PartialRelation.java
index f884f8d6..9bae53a9 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/representation/PartialRelation.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/representation/PartialRelation.java
@@ -1,18 +1,16 @@
1package tools.refinery.store.reasoning.representation; 1package tools.refinery.store.reasoning.representation;
2 2
3import tools.refinery.store.reasoning.literal.Modality; 3import tools.refinery.store.query.Constraint;
4import tools.refinery.store.reasoning.literal.PartialRelationLiteral; 4import tools.refinery.store.query.term.NodeSort;
5import tools.refinery.store.reasoning.literal.ModalRelationLiteral; 5import tools.refinery.store.query.term.Sort;
6import tools.refinery.store.query.RelationLike;
7import tools.refinery.store.query.Variable;
8import tools.refinery.store.query.literal.CallPolarity;
9import tools.refinery.store.representation.AbstractDomain; 6import tools.refinery.store.representation.AbstractDomain;
10import tools.refinery.store.representation.TruthValue; 7import tools.refinery.store.representation.TruthValue;
11import tools.refinery.store.representation.TruthValueDomain; 8import tools.refinery.store.representation.TruthValueDomain;
12 9
10import java.util.Arrays;
13import java.util.List; 11import java.util.List;
14 12
15public record PartialRelation(String name, int arity) implements PartialSymbol<TruthValue, Boolean>, RelationLike { 13public record PartialRelation(String name, int arity) implements PartialSymbol<TruthValue, Boolean>, Constraint {
16 @Override 14 @Override
17 public AbstractDomain<TruthValue, Boolean> abstractDomain() { 15 public AbstractDomain<TruthValue, Boolean> abstractDomain() {
18 return TruthValueDomain.INSTANCE; 16 return TruthValueDomain.INSTANCE;
@@ -28,24 +26,16 @@ public record PartialRelation(String name, int arity) implements PartialSymbol<T
28 return false; 26 return false;
29 } 27 }
30 28
31 public ModalRelationLiteral call(CallPolarity polarity, Modality modality, List<Variable> arguments) { 29 @Override
32 return new ModalRelationLiteral(polarity, modality, this, arguments); 30 public List<Sort> getSorts() {
33 } 31 var sorts = new Sort[arity()];
34 32 Arrays.fill(sorts, NodeSort.INSTANCE);
35 public PartialRelationLiteral call(CallPolarity polarity, List<Variable> arguments) { 33 return List.of(sorts);
36 return new PartialRelationLiteral(polarity, this, arguments);
37 }
38
39 public PartialRelationLiteral call(CallPolarity polarity, Variable... arguments) {
40 return call(polarity, List.of(arguments));
41 }
42
43 public PartialRelationLiteral call(Variable... arguments) {
44 return call(CallPolarity.POSITIVE, arguments);
45 } 34 }
46 35
47 public PartialRelationLiteral callTransitive(Variable left, Variable right) { 36 @Override
48 return call(CallPolarity.TRANSITIVE, List.of(left, right)); 37 public String toReferenceString() {
38 return name;
49 } 39 }
50 40
51 @Override 41 @Override
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RelationRefinementAction.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RelationRefinementAction.java
index c7681b53..e8ed05a3 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RelationRefinementAction.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RelationRefinementAction.java
@@ -3,7 +3,7 @@ package tools.refinery.store.reasoning.rule;
3import tools.refinery.store.reasoning.Reasoning; 3import tools.refinery.store.reasoning.Reasoning;
4import tools.refinery.store.reasoning.representation.PartialRelation; 4import tools.refinery.store.reasoning.representation.PartialRelation;
5import tools.refinery.store.model.Model; 5import tools.refinery.store.model.Model;
6import tools.refinery.store.query.Variable; 6import tools.refinery.store.query.term.Variable;
7import tools.refinery.store.representation.TruthValue; 7import tools.refinery.store.representation.TruthValue;
8import tools.refinery.store.tuple.Tuple; 8import tools.refinery.store.tuple.Tuple;
9 9
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/Rule.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/Rule.java
index 8a812518..c7b16d47 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/Rule.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/Rule.java
@@ -1,7 +1,7 @@
1package tools.refinery.store.reasoning.rule; 1package tools.refinery.store.reasoning.rule;
2 2
3import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.Dnf; 4import tools.refinery.store.query.dnf.Dnf;
5 5
6import java.util.ArrayList; 6import java.util.ArrayList;
7import java.util.HashSet; 7import java.util.HashSet;
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleAction.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleAction.java
index bf980759..4753b8bc 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleAction.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/rule/RuleAction.java
@@ -1,7 +1,7 @@
1package tools.refinery.store.reasoning.rule; 1package tools.refinery.store.reasoning.rule;
2 2
3import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.Variable; 4import tools.refinery.store.query.term.Variable;
5 5
6import java.util.List; 6import java.util.List;
7 7
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java
index 042c2636..90633495 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/Seed.java
@@ -1,4 +1,4 @@
1package tools.refinery.store.reasoning.translator; 1package tools.refinery.store.reasoning.seed;
2 2
3import tools.refinery.store.map.Cursor; 3import tools.refinery.store.map.Cursor;
4import tools.refinery.store.tuple.Tuple; 4import tools.refinery.store.tuple.Tuple;
@@ -6,7 +6,7 @@ import tools.refinery.store.tuple.Tuple;
6public interface Seed<T> { 6public interface Seed<T> {
7 int arity(); 7 int arity();
8 8
9 T getReducedValue(); 9 T reducedValue();
10 10
11 T get(Tuple key); 11 T get(Tuple key);
12 12
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/UniformSeed.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/UniformSeed.java
new file mode 100644
index 00000000..a030f6ea
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/seed/UniformSeed.java
@@ -0,0 +1,22 @@
1package tools.refinery.store.reasoning.seed;
2
3import tools.refinery.store.map.Cursor;
4import tools.refinery.store.tuple.Tuple;
5
6public record UniformSeed<T>(int arity, T reducedValue) implements Seed<T> {
7 public UniformSeed {
8 if (arity < 0) {
9 throw new IllegalArgumentException("Arity must not be negative");
10 }
11 }
12
13 @Override
14 public T get(Tuple key) {
15 return reducedValue;
16 }
17
18 @Override
19 public Cursor<Tuple, T> getCursor(T defaultValue, int nodeCount) {
20 return null;
21 }
22}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/Advice.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/Advice.java
index 7909a7e1..5cdfedf7 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/Advice.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/Advice.java
@@ -2,7 +2,7 @@ package tools.refinery.store.reasoning.translator;
2 2
3import tools.refinery.store.reasoning.representation.AnyPartialSymbol; 3import tools.refinery.store.reasoning.representation.AnyPartialSymbol;
4import tools.refinery.store.reasoning.representation.PartialRelation; 4import tools.refinery.store.reasoning.representation.PartialRelation;
5import tools.refinery.store.query.Variable; 5import tools.refinery.store.query.term.Variable;
6import tools.refinery.store.query.literal.Literal; 6import tools.refinery.store.query.literal.Literal;
7import tools.refinery.store.query.substitution.Substitutions; 7import tools.refinery.store.query.substitution.Substitutions;
8 8
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/TranslatedRelation.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/TranslatedRelation.java
new file mode 100644
index 00000000..9bab80c9
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/TranslatedRelation.java
@@ -0,0 +1,22 @@
1package tools.refinery.store.reasoning.translator;
2
3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.term.Variable;
5import tools.refinery.store.query.literal.CallPolarity;
6import tools.refinery.store.query.literal.Literal;
7import tools.refinery.store.reasoning.PartialInterpretation;
8import tools.refinery.store.reasoning.literal.Modality;
9import tools.refinery.store.reasoning.representation.PartialRelation;
10import tools.refinery.store.representation.TruthValue;
11
12import java.util.List;
13
14public interface TranslatedRelation {
15 PartialRelation getSource();
16
17 void configure(List<Advice> advices);
18
19 List<Literal> call(CallPolarity polarity, Modality modality, List<Variable> arguments);
20
21 PartialInterpretation<TruthValue, Boolean> createPartialInterpretation(Model model);
22}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/TranslationUnit.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/TranslationUnit.java
index e45d20c8..24b93911 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/TranslationUnit.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/TranslationUnit.java
@@ -1,48 +1,32 @@
1package tools.refinery.store.reasoning.translator; 1package tools.refinery.store.reasoning.translator;
2 2
3import tools.refinery.store.reasoning.ReasoningBuilder;
4import tools.refinery.store.model.Model; 3import tools.refinery.store.model.Model;
5import tools.refinery.store.model.ModelStoreBuilder; 4import tools.refinery.store.model.ModelStoreBuilder;
6import tools.refinery.store.reasoning.AnyPartialInterpretation; 5import tools.refinery.store.reasoning.ReasoningBuilder;
7import tools.refinery.store.reasoning.literal.Modality;
8import tools.refinery.store.reasoning.representation.AnyPartialSymbol;
9import tools.refinery.store.reasoning.representation.PartialRelation;
10import tools.refinery.store.query.Variable;
11import tools.refinery.store.query.literal.CallPolarity;
12import tools.refinery.store.query.literal.Literal;
13 6
14import java.util.Collection; 7import java.util.Collection;
15import java.util.List;
16import java.util.Map;
17 8
18public abstract class TranslationUnit { 9public abstract class TranslationUnit {
19 private ReasoningBuilder reasoningBuilder; 10 private ReasoningBuilder reasoningBuilder;
20 11
21 protected ReasoningBuilder getPartialInterpretationBuilder() { 12 protected ReasoningBuilder getReasoningBuilder() {
22 return reasoningBuilder; 13 return reasoningBuilder;
23 } 14 }
24 15
25 public void setPartialInterpretationBuilder(ReasoningBuilder reasoningBuilder) { 16 public void setPartialInterpretationBuilder(ReasoningBuilder reasoningBuilder) {
26 this.reasoningBuilder = reasoningBuilder; 17 this.reasoningBuilder = reasoningBuilder;
18 configureReasoningBuilder();
27 } 19 }
28 20
29 protected ModelStoreBuilder getModelStoreBuilder() { 21 protected ModelStoreBuilder getModelStoreBuilder() {
30 return reasoningBuilder.getStoreBuilder(); 22 return reasoningBuilder.getStoreBuilder();
31 } 23 }
32 24
33 public abstract Collection<AnyPartialSymbol> getTranslatedPartialSymbols(); 25 protected void configureReasoningBuilder() {
34 26 // Nothing to configure by default.
35 public Collection<Advice> computeAdvices() {
36 // No advices to give by default.
37 return List.of();
38 } 27 }
39 28
40 public abstract void configure(Collection<Advice> advices); 29 public abstract Collection<TranslatedRelation> getTranslatedRelations();
41
42 public abstract List<Literal> call(CallPolarity polarity, Modality modality, PartialRelation target,
43 List<Variable> arguments);
44
45 public abstract Map<AnyPartialSymbol, AnyPartialInterpretation> createPartialInterpretations(Model model);
46 30
47 public abstract void initializeModel(Model model, int nodeCount); 31 public abstract void initializeModel(Model model, int nodeCount);
48} 32}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/BaseDecisionInterpretation.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/BaseDecisionInterpretation.java
new file mode 100644
index 00000000..b703f142
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/BaseDecisionInterpretation.java
@@ -0,0 +1,88 @@
1package tools.refinery.store.reasoning.translator.base;
2
3import tools.refinery.store.map.Cursor;
4import tools.refinery.store.model.Interpretation;
5import tools.refinery.store.query.ResultSet;
6import tools.refinery.store.reasoning.MergeResult;
7import tools.refinery.store.reasoning.PartialInterpretation;
8import tools.refinery.store.reasoning.ReasoningAdapter;
9import tools.refinery.store.reasoning.representation.PartialRelation;
10import tools.refinery.store.representation.TruthValue;
11import tools.refinery.store.tuple.Tuple;
12
13public class BaseDecisionInterpretation implements PartialInterpretation<TruthValue, Boolean> {
14 private final ReasoningAdapter reasoningAdapter;
15 private PartialRelation partialRelation;
16 private final ResultSet<Boolean> mustResultSet;
17 private final ResultSet<Boolean> mayResultSet;
18 private final ResultSet<Boolean> errorResultSet;
19 private final ResultSet<Boolean> currentResultSet;
20 private final Interpretation<TruthValue> interpretation;
21
22 public BaseDecisionInterpretation(ReasoningAdapter reasoningAdapter, ResultSet<Boolean> mustResultSet,
23 ResultSet<Boolean> mayResultSet, ResultSet<Boolean> errorResultSet,
24 ResultSet<Boolean> currentResultSet, Interpretation<TruthValue> interpretation) {
25 this.reasoningAdapter = reasoningAdapter;
26 this.mustResultSet = mustResultSet;
27 this.mayResultSet = mayResultSet;
28 this.errorResultSet = errorResultSet;
29 this.currentResultSet = currentResultSet;
30 this.interpretation = interpretation;
31 }
32
33 @Override
34 public ReasoningAdapter getAdapter() {
35 return reasoningAdapter;
36 }
37
38 @Override
39 public int countUnfinished() {
40 return 0;
41 }
42
43 @Override
44 public int countErrors() {
45 return errorResultSet.size();
46 }
47
48 @Override
49 public PartialRelation getPartialSymbol() {
50 return partialRelation;
51 }
52
53 @Override
54 public TruthValue get(Tuple key) {
55 return null;
56 }
57
58 @Override
59 public Cursor<Tuple, TruthValue> getAll() {
60 return null;
61 }
62
63 @Override
64 public MergeResult merge(Tuple key, TruthValue value) {
65 TruthValue newValue;
66 switch (value) {
67 case UNKNOWN -> {
68 return MergeResult.UNCHANGED;
69 }
70 case TRUE -> newValue = mayResultSet.get(key) ? TruthValue.TRUE : TruthValue.ERROR;
71 case FALSE -> newValue = mustResultSet.get(key) ? TruthValue.ERROR : TruthValue.FALSE;
72 case ERROR -> newValue = TruthValue.ERROR;
73 default -> throw new IllegalArgumentException("Unknown truth value: " + value);
74 }
75 var oldValue = interpretation.put(key, newValue);
76 return oldValue == TruthValue.ERROR ? MergeResult.UNCHANGED : MergeResult.REFINED;
77 }
78
79 @Override
80 public Boolean getConcrete(Tuple key) {
81 return currentResultSet.get(key);
82 }
83
84 @Override
85 public Cursor<Tuple, Boolean> getAllConcrete() {
86 return null;
87 }
88}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/BaseDecisionTranslationUnit.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/BaseDecisionTranslationUnit.java
new file mode 100644
index 00000000..36e2782a
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/BaseDecisionTranslationUnit.java
@@ -0,0 +1,49 @@
1package tools.refinery.store.reasoning.translator.base;
2
3import tools.refinery.store.model.Model;
4import tools.refinery.store.reasoning.representation.PartialRelation;
5import tools.refinery.store.reasoning.seed.Seed;
6import tools.refinery.store.reasoning.seed.UniformSeed;
7import tools.refinery.store.reasoning.translator.TranslatedRelation;
8import tools.refinery.store.reasoning.translator.TranslationUnit;
9import tools.refinery.store.representation.Symbol;
10import tools.refinery.store.representation.TruthValue;
11
12import java.util.Collection;
13import java.util.List;
14
15public class BaseDecisionTranslationUnit extends TranslationUnit {
16 private final PartialRelation partialRelation;
17 private final Seed<TruthValue> seed;
18 private final Symbol<TruthValue> symbol;
19
20 public BaseDecisionTranslationUnit(PartialRelation partialRelation, Seed<TruthValue> seed) {
21 if (seed.arity() != partialRelation.arity()) {
22 throw new IllegalArgumentException("Expected seed with arity %d for %s, got arity %s"
23 .formatted(partialRelation.arity(), partialRelation, seed.arity()));
24 }
25 this.partialRelation = partialRelation;
26 this.seed = seed;
27 symbol = new Symbol<>(partialRelation.name(), partialRelation.arity(), TruthValue.class, TruthValue.UNKNOWN);
28 }
29
30 public BaseDecisionTranslationUnit(PartialRelation partialRelation) {
31 this(partialRelation, new UniformSeed<>(partialRelation.arity(), TruthValue.UNKNOWN));
32 }
33
34 @Override
35 protected void configureReasoningBuilder() {
36 getModelStoreBuilder().symbol(symbol);
37 }
38
39 @Override
40 public Collection<TranslatedRelation> getTranslatedRelations() {
41 return List.of(new TranslatedBaseDecision(getReasoningBuilder(), partialRelation, symbol));
42 }
43
44 @Override
45 public void initializeModel(Model model, int nodeCount) {
46 var interpretation = model.getInterpretation(symbol);
47 interpretation.putAll(seed.getCursor(TruthValue.UNKNOWN, nodeCount));
48 }
49}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/TranslatedBaseDecision.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/TranslatedBaseDecision.java
new file mode 100644
index 00000000..2294b4fd
--- /dev/null
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/base/TranslatedBaseDecision.java
@@ -0,0 +1,49 @@
1package tools.refinery.store.reasoning.translator.base;
2
3import tools.refinery.store.model.Model;
4import tools.refinery.store.query.term.Variable;
5import tools.refinery.store.query.literal.CallPolarity;
6import tools.refinery.store.query.literal.Literal;
7import tools.refinery.store.reasoning.PartialInterpretation;
8import tools.refinery.store.reasoning.ReasoningBuilder;
9import tools.refinery.store.reasoning.literal.Modality;
10import tools.refinery.store.reasoning.representation.PartialRelation;
11import tools.refinery.store.reasoning.translator.Advice;
12import tools.refinery.store.reasoning.translator.TranslatedRelation;
13import tools.refinery.store.representation.Symbol;
14import tools.refinery.store.representation.TruthValue;
15
16import java.util.List;
17
18class TranslatedBaseDecision implements TranslatedRelation {
19 private final ReasoningBuilder reasoningBuilder;
20 private final PartialRelation partialRelation;
21 private final Symbol<TruthValue> symbol;
22
23 public TranslatedBaseDecision(ReasoningBuilder reasoningBuilder, PartialRelation partialRelation,
24 Symbol<TruthValue> symbol) {
25 this.reasoningBuilder = reasoningBuilder;
26 this.partialRelation = partialRelation;
27 this.symbol = symbol;
28 }
29
30 @Override
31 public PartialRelation getSource() {
32 return partialRelation;
33 }
34
35 @Override
36 public void configure(List<Advice> advices) {
37
38 }
39
40 @Override
41 public List<Literal> call(CallPolarity polarity, Modality modality, List<Variable> arguments) {
42 return null;
43 }
44
45 @Override
46 public PartialInterpretation<TruthValue, Boolean> createPartialInterpretation(Model model) {
47 return null;
48 }
49}
diff --git a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslationUnit.java b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslationUnit.java
index c800b4cd..4b0761f2 100644
--- a/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslationUnit.java
+++ b/subprojects/store-reasoning/src/main/java/tools/refinery/store/reasoning/translator/typehierarchy/TypeHierarchyTranslationUnit.java
@@ -1,18 +1,14 @@
1package tools.refinery.store.reasoning.translator.typehierarchy; 1package tools.refinery.store.reasoning.translator.typehierarchy;
2 2
3import tools.refinery.store.reasoning.AnyPartialInterpretation; 3import tools.refinery.store.model.Model;
4import tools.refinery.store.reasoning.literal.Modality;
5import tools.refinery.store.reasoning.representation.AnyPartialSymbol;
6import tools.refinery.store.reasoning.representation.PartialRelation; 4import tools.refinery.store.reasoning.representation.PartialRelation;
7import tools.refinery.store.reasoning.translator.Advice; 5import tools.refinery.store.reasoning.translator.TranslatedRelation;
8import tools.refinery.store.reasoning.translator.TranslationUnit; 6import tools.refinery.store.reasoning.translator.TranslationUnit;
9import tools.refinery.store.model.Model;
10import tools.refinery.store.query.Variable;
11import tools.refinery.store.query.literal.CallPolarity;
12import tools.refinery.store.query.literal.Literal;
13import tools.refinery.store.representation.Symbol; 7import tools.refinery.store.representation.Symbol;
14 8
15import java.util.*; 9import java.util.Collection;
10import java.util.List;
11import java.util.Map;
16 12
17public class TypeHierarchyTranslationUnit extends TranslationUnit { 13public class TypeHierarchyTranslationUnit extends TranslationUnit {
18 static final Symbol<InferredType> INFERRED_TYPE_SYMBOL = new Symbol<>("inferredType", 1, 14 static final Symbol<InferredType> INFERRED_TYPE_SYMBOL = new Symbol<>("inferredType", 1,
@@ -25,24 +21,8 @@ public class TypeHierarchyTranslationUnit extends TranslationUnit {
25 } 21 }
26 22
27 @Override 23 @Override
28 public Collection<AnyPartialSymbol> getTranslatedPartialSymbols() { 24 public Collection<TranslatedRelation> getTranslatedRelations() {
29 return null; 25 return List.of();
30 }
31
32 @Override
33 public void configure(Collection<Advice> advices) {
34
35 }
36
37 @Override
38 public List<Literal> call(CallPolarity polarity, Modality modality, PartialRelation target,
39 List<Variable> arguments) {
40 return null;
41 }
42
43 @Override
44 public Map<AnyPartialSymbol, AnyPartialInterpretation> createPartialInterpretations(Model model) {
45 return null;
46 } 26 }
47 27
48 @Override 28 @Override
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleLike.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleLike.java
index 470ca298..953ea9f8 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleLike.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleLike.java
@@ -1,5 +1,8 @@
1package tools.refinery.store.tuple; 1package tools.refinery.store.tuple;
2 2
3import java.util.stream.Collectors;
4import java.util.stream.IntStream;
5
3public interface TupleLike { 6public interface TupleLike {
4 int getSize(); 7 int getSize();
5 8
@@ -22,4 +25,11 @@ public interface TupleLike {
22 default -> Tuple.of(toArray()); 25 default -> Tuple.of(toArray());
23 }; 26 };
24 } 27 }
28
29 static String toString(TupleLike tuple) {
30 var valuesString = IntStream.range(0, tuple.getSize())
31 .mapToObj(i -> Integer.toString(tuple.get(i)))
32 .collect(Collectors.joining(", "));
33 return "[" + valuesString + "]";
34 }
25} 35}
diff --git a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java
index 15fd063b..c3aed847 100644
--- a/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java
+++ b/subprojects/store/src/main/java/tools/refinery/store/tuple/TupleN.java
@@ -1,7 +1,6 @@
1package tools.refinery.store.tuple; 1package tools.refinery.store.tuple;
2 2
3import java.util.Arrays; 3import java.util.Arrays;
4import java.util.stream.Collectors;
5 4
6public record TupleN(int[] values) implements Tuple { 5public record TupleN(int[] values) implements Tuple {
7 static final int CUSTOM_TUPLE_SIZE = 2; 6 static final int CUSTOM_TUPLE_SIZE = 2;
@@ -29,8 +28,7 @@ public record TupleN(int[] values) implements Tuple {
29 28
30 @Override 29 @Override
31 public String toString() { 30 public String toString() {
32 var valuesString = Arrays.stream(values).mapToObj(Integer::toString).collect(Collectors.joining(", ")); 31 return TupleLike.toString(this);
33 return "[" + valuesString + "]";
34 } 32 }
35 33
36 @Override 34 @Override