From 9adbb3d49899a87b3026c11cb4ba3ff77f4fb75b Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sat, 19 Aug 2023 02:31:57 +0200 Subject: chore: import VIATRA source Make our modifications more maintainable by editing the source code directly instead of using reflection. --- subprojects/viatra-runtime-rete/about.html | 26 + subprojects/viatra-runtime-rete/build.gradle.kts | 15 + .../aggregation/AbstractColumnAggregatorNode.java | 474 +++++++++++ .../rete/aggregation/ColumnAggregatorNode.java | 369 ++++++++ .../viatra/runtime/rete/aggregation/CountNode.java | 38 + .../runtime/rete/aggregation/GroupedMap.java | 120 +++ .../runtime/rete/aggregation/GroupedSet.java | 114 +++ .../runtime/rete/aggregation/IAggregatorNode.java | 26 + .../aggregation/IndexerBasedAggregatorNode.java | 278 ++++++ ...FaithfulParallelTimelyColumnAggregatorNode.java | 217 +++++ ...ithfulSequentialTimelyColumnAggregatorNode.java | 280 ++++++ .../timely/FaithfulTimelyColumnAggregatorNode.java | 247 ++++++ ...irstOnlyParallelTimelyColumnAggregatorNode.java | 106 +++ ...stOnlySequentialTimelyColumnAggregatorNode.java | 117 +++ .../FirstOnlyTimelyColumnAggregatorNode.java | 212 +++++ .../runtime/rete/boundary/Disconnectable.java | 26 + .../rete/boundary/ExternalInputEnumeratorNode.java | 209 +++++ .../boundary/ExternalInputStatelessFilterNode.java | 68 ++ .../runtime/rete/boundary/InputConnector.java | 208 +++++ .../viatra/runtime/rete/boundary/ReteBoundary.java | 551 ++++++++++++ .../construction/RetePatternBuildException.java | 50 ++ .../basiclinear/BasicLinearLayout.java | 171 ++++ .../basiclinear/OrderingHeuristics.java | 90 ++ .../construction/plancompiler/CompilerHelper.java | 390 +++++++++ .../plancompiler/RecursionCutoffPoint.java | 86 ++ .../plancompiler/ReteRecipeCompiler.java | 948 +++++++++++++++++++++ .../rete/construction/quasitree/JoinCandidate.java | 162 ++++ .../quasitree/JoinOrderingHeuristics.java | 49 ++ .../construction/quasitree/QuasiTreeLayout.java | 205 +++++ .../rete/construction/quasitree/TieBreaker.java | 30 + .../runtime/rete/eval/AbstractEvaluatorNode.java | 65 ++ .../viatra/runtime/rete/eval/EvaluatorCore.java | 180 ++++ .../viatra/runtime/rete/eval/IEvaluatorNode.java | 25 + .../runtime/rete/eval/MemorylessEvaluatorNode.java | 75 ++ .../rete/eval/OutputCachingEvaluatorNode.java | 311 +++++++ .../runtime/rete/eval/RelationEvaluatorNode.java | 183 ++++ .../runtime/rete/index/DefaultIndexerListener.java | 28 + .../viatra/runtime/rete/index/DualInputNode.java | 348 ++++++++ .../viatra/runtime/rete/index/ExistenceNode.java | 199 +++++ .../rete/index/GenericProjectionIndexer.java | 76 ++ .../viatra/runtime/rete/index/IdentityIndexer.java | 76 ++ .../viatra/runtime/rete/index/Indexer.java | 71 ++ .../viatra/runtime/rete/index/IndexerListener.java | 41 + .../runtime/rete/index/IndexerWithMemory.java | 284 ++++++ .../viatra/runtime/rete/index/IterableIndexer.java | 34 + .../viatra/runtime/rete/index/JoinNode.java | 193 +++++ .../runtime/rete/index/MemoryIdentityIndexer.java | 55 ++ .../runtime/rete/index/MemoryNullIndexer.java | 54 ++ .../viatra/runtime/rete/index/NullIndexer.java | 88 ++ .../viatra/runtime/rete/index/OnetimeIndexer.java | 47 + .../runtime/rete/index/ProjectionIndexer.java | 21 + .../rete/index/SpecializedProjectionIndexer.java | 176 ++++ .../viatra/runtime/rete/index/StandardIndexer.java | 127 +++ .../rete/index/TransitiveClosureNodeIndexer.java | 121 +++ .../index/timely/TimelyMemoryIdentityIndexer.java | 51 ++ .../rete/index/timely/TimelyMemoryNullIndexer.java | 49 ++ .../rete/matcher/DRedReteBackendFactory.java | 49 ++ .../runtime/rete/matcher/HintConfigurator.java | 46 + .../rete/matcher/IncrementalMatcherCapability.java | 30 + .../runtime/rete/matcher/ReteBackendFactory.java | 100 +++ .../rete/matcher/ReteBackendFactoryProvider.java | 35 + .../viatra/runtime/rete/matcher/ReteEngine.java | 579 +++++++++++++ .../runtime/rete/matcher/RetePatternMatcher.java | 462 ++++++++++ .../runtime/rete/matcher/TimelyConfiguration.java | 61 ++ .../rete/matcher/TimelyReteBackendFactory.java | 64 ++ .../refinery/viatra/runtime/rete/misc/Bag.java | 43 + .../viatra/runtime/rete/misc/ConstantNode.java | 50 ++ .../runtime/rete/misc/DefaultDeltaMonitor.java | 43 + .../viatra/runtime/rete/misc/DeltaMonitor.java | 111 +++ .../viatra/runtime/rete/misc/SimpleReceiver.java | 109 +++ .../viatra/runtime/rete/network/BaseNode.java | 108 +++ .../runtime/rete/network/ConnectionFactory.java | 170 ++++ .../viatra/runtime/rete/network/IGroupable.java | 31 + .../viatra/runtime/rete/network/Network.java | 408 +++++++++ .../NetworkStructureChangeSensitiveNode.java | 30 + .../refinery/viatra/runtime/rete/network/Node.java | 62 ++ .../viatra/runtime/rete/network/NodeFactory.java | 375 ++++++++ .../runtime/rete/network/NodeProvisioner.java | 346 ++++++++ .../runtime/rete/network/PosetAwareReceiver.java | 39 + .../runtime/rete/network/ProductionNode.java | 28 + .../viatra/runtime/rete/network/Receiver.java | 85 ++ .../runtime/rete/network/RederivableNode.java | 34 + .../runtime/rete/network/ReinitializedNode.java | 14 + .../viatra/runtime/rete/network/ReteContainer.java | 729 ++++++++++++++++ .../viatra/runtime/rete/network/StandardNode.java | 121 +++ .../viatra/runtime/rete/network/Supplier.java | 82 ++ .../viatra/runtime/rete/network/Tunnel.java | 19 + .../viatra/runtime/rete/network/UpdateMessage.java | 31 + .../network/communication/CommunicationGroup.java | 103 +++ .../communication/CommunicationTracker.java | 467 ++++++++++ .../network/communication/MessageSelector.java | 19 + .../rete/network/communication/NodeComparator.java | 32 + .../rete/network/communication/PhasedSelector.java | 34 + .../rete/network/communication/Timestamp.java | 124 +++ .../timeless/RecursiveCommunicationGroup.java | 164 ++++ .../timeless/SingletonCommunicationGroup.java | 86 ++ .../timeless/TimelessCommunicationTracker.java | 149 ++++ .../communication/timely/ResumableNode.java | 36 + .../timely/TimelyCommunicationGroup.java | 171 ++++ .../timely/TimelyCommunicationTracker.java | 216 +++++ .../timely/TimelyIndexerListenerProxy.java | 81 ++ .../communication/timely/TimelyMailboxProxy.java | 102 +++ .../timely/TimestampTransformation.java | 48 ++ .../rete/network/delayed/DelayedCommand.java | 81 ++ .../network/delayed/DelayedConnectCommand.java | 27 + .../network/delayed/DelayedDisconnectCommand.java | 30 + .../network/indexer/DefaultMessageIndexer.java | 74 ++ .../network/indexer/GroupBasedMessageIndexer.java | 95 +++ .../rete/network/indexer/MessageIndexer.java | 33 + .../rete/network/mailbox/AdaptableMailbox.java | 32 + .../network/mailbox/FallThroughCapableMailbox.java | 30 + .../runtime/rete/network/mailbox/Mailbox.java | 78 ++ .../network/mailbox/MessageIndexerFactory.java | 23 + .../timeless/AbstractUpdateSplittingMailbox.java | 109 +++ .../mailbox/timeless/BehaviorChangingMailbox.java | 117 +++ .../network/mailbox/timeless/DefaultMailbox.java | 164 ++++ .../mailbox/timeless/PosetAwareMailbox.java | 218 +++++ .../mailbox/timeless/UpdateSplittingMailbox.java | 135 +++ .../rete/network/mailbox/timely/TimelyMailbox.java | 150 ++++ .../viatra/runtime/rete/remote/Address.java | 125 +++ .../viatra/runtime/rete/remote/RemoteReceiver.java | 63 ++ .../viatra/runtime/rete/remote/RemoteSupplier.java | 54 ++ .../single/AbstractUniquenessEnforcerNode.java | 138 +++ .../viatra/runtime/rete/single/CallbackNode.java | 37 + .../runtime/rete/single/DefaultProductionNode.java | 79 ++ .../rete/single/DiscriminatorBucketNode.java | 85 ++ .../rete/single/DiscriminatorDispatcherNode.java | 154 ++++ .../runtime/rete/single/EqualityFilterNode.java | 41 + .../viatra/runtime/rete/single/FilterNode.java | 69 ++ .../runtime/rete/single/InequalityFilterNode.java | 52 ++ .../rete/single/RepresentativeElectionNode.java | 125 +++ .../runtime/rete/single/SingleInputNode.java | 126 +++ .../runtime/rete/single/TimelyProductionNode.java | 63 ++ .../rete/single/TimelyUniquenessEnforcerNode.java | 161 ++++ .../runtime/rete/single/TransformerNode.java | 49 ++ .../runtime/rete/single/TransitiveClosureNode.java | 147 ++++ .../runtime/rete/single/TransparentNode.java | 48 ++ .../viatra/runtime/rete/single/TrimmerNode.java | 61 ++ .../rete/single/UniquenessEnforcerNode.java | 321 +++++++ .../runtime/rete/single/ValueBinderFilterNode.java | 44 + .../rete/traceability/ActiveNodeConflictTrace.java | 24 + .../runtime/rete/traceability/CompiledQuery.java | 54 ++ .../runtime/rete/traceability/CompiledSubPlan.java | 51 ++ .../traceability/ParameterProjectionTrace.java | 42 + .../rete/traceability/PatternTraceInfo.java | 17 + .../runtime/rete/traceability/PlanningTrace.java | 80 ++ .../runtime/rete/traceability/RecipeTraceInfo.java | 81 ++ .../runtime/rete/traceability/TraceInfo.java | 33 + .../rete/traceability/UserRequestTrace.java | 36 + .../runtime/rete/util/LexicographicComparator.java | 58 ++ .../refinery/viatra/runtime/rete/util/Options.java | 111 +++ .../runtime/rete/util/OrderingCompareAgent.java | 92 ++ .../viatra/runtime/rete/util/ReteHintOptions.java | 60 ++ 153 files changed, 19363 insertions(+) create mode 100644 subprojects/viatra-runtime-rete/about.html create mode 100644 subprojects/viatra-runtime-rete/build.gradle.kts create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java (limited to 'subprojects/viatra-runtime-rete') diff --git a/subprojects/viatra-runtime-rete/about.html b/subprojects/viatra-runtime-rete/about.html new file mode 100644 index 00000000..d1d5593a --- /dev/null +++ b/subprojects/viatra-runtime-rete/about.html @@ -0,0 +1,26 @@ + + + + +About + + + +

About This Content

+ +

March 18, 2019

+

License

+ +

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the +Eclipse Public License Version 2.0 ("EPL"). A copy of the EPL is available at http://www.eclipse.org/legal/epl-v20.html. +For purposes of the EPL, "Program" will mean the Content.

+ +

If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may +apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise +indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at http://www.eclipse.org.

+ + diff --git a/subprojects/viatra-runtime-rete/build.gradle.kts b/subprojects/viatra-runtime-rete/build.gradle.kts new file mode 100644 index 00000000..7e795a90 --- /dev/null +++ b/subprojects/viatra-runtime-rete/build.gradle.kts @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ + +plugins { + id("tools.refinery.gradle.java-library") +} + +dependencies { + implementation(project(":refinery-viatra-runtime")) + implementation(project(":refinery-viatra-runtime-rete-recipes")) + implementation(libs.slf4j.log4j) +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java new file mode 100644 index 00000000..2588bde1 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/AbstractColumnAggregatorNode.java @@ -0,0 +1,474 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; + +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.Clearable; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.index.Indexer; +import tools.refinery.viatra.runtime.rete.index.StandardIndexer; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.single.SingleInputNode; + +/** + * Groups incoming tuples by the given mask, and aggregates values at a specific index in each group. + *

+ * Direct children are not supported, use via outer join indexers instead. + *

+ * There are both timeless and timely implementations. + * + * @author Tamas Szabo + * @since 2.2 + * + */ +public abstract class AbstractColumnAggregatorNode extends SingleInputNode + implements Clearable, IAggregatorNode { + + /** + * @since 1.6 + */ + protected final IMultisetAggregationOperator operator; + + /** + * @since 1.6 + */ + protected final TupleMask groupMask; + + /** + * @since 1.6 + */ + protected final TupleMask columnMask; + + /** + * @since 1.6 + */ + protected final int sourceWidth; + + /** + * @since 1.6 + */ + protected final IQueryRuntimeContext runtimeContext; + + protected final AggregateResult NEUTRAL; + + protected AggregatorOuterIndexer aggregatorOuterIndexer; + + @SuppressWarnings("rawtypes") + protected AbstractColumnAggregatorNode.AggregatorOuterIdentityIndexer[] aggregatorOuterIdentityIndexers; + + /** + * Creates a new column aggregator node. + * + * @param reteContainer + * the RETE container of the node + * @param operator + * the aggregation operator + * @param deleteRederiveEvaluation + * true if the node should run in DRED mode, false otherwise + * @param groupMask + * the mask that masks a tuple to obtain the key that we are grouping-by + * @param columnMask + * the mask that masks a tuple to obtain the tuple element(s) that we are aggregating over + * @param posetComparator + * the poset comparator for the column, if known, otherwise it can be null + * @since 1.6 + */ + public AbstractColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final TupleMask groupMask, final TupleMask columnMask) { + super(reteContainer); + this.operator = operator; + this.groupMask = groupMask; + this.columnMask = columnMask; + this.sourceWidth = groupMask.indices.length; + this.runtimeContext = reteContainer.getNetwork().getEngine().getRuntimeContext(); + this.NEUTRAL = operator.getAggregate(operator.createNeutral()); + reteContainer.registerClearable(this); + } + + /** + * Creates a new column aggregator node. + * + * @param reteContainer + * the RETE container of the node + * @param operator + * the aggregation operator + * @param groupMask + * the mask that masks a tuple to obtain the key that we are grouping-by + * @param aggregatedColumn + * the index of the column that the aggregator node is aggregating over + */ + public AbstractColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final TupleMask groupMask, final int aggregatedColumn) { + this(reteContainer, operator, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth)); + } + + @Override + public CommunicationTracker getCommunicationTracker() { + return this.reteContainer.getCommunicationTracker(); + } + + @Override + public void pullInto(Collection collector, boolean flush) { + // DIRECT CHILDREN NOT SUPPORTED + throw new UnsupportedOperationException(); + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + // DIRECT CHILDREN NOT SUPPORTED + throw new UnsupportedOperationException(); + } + + @Override + public void appendChild(Receiver receiver) { + // DIRECT CHILDREN NOT SUPPORTED + throw new UnsupportedOperationException(); + } + + @Override + public Indexer getAggregatorOuterIndexer() { + if (aggregatorOuterIndexer == null) { + aggregatorOuterIndexer = new AggregatorOuterIndexer(); + this.getCommunicationTracker().registerDependency(this, aggregatorOuterIndexer); + } + return aggregatorOuterIndexer; + } + + @Override + public Indexer getAggregatorOuterIdentityIndexer(final int resultPositionInSignature) { + if (aggregatorOuterIdentityIndexers == null) { + aggregatorOuterIdentityIndexers = new AbstractColumnAggregatorNode.AggregatorOuterIdentityIndexer[sourceWidth + + 1]; + } + if (aggregatorOuterIdentityIndexers[resultPositionInSignature] == null) { + aggregatorOuterIdentityIndexers[resultPositionInSignature] = new AggregatorOuterIdentityIndexer( + resultPositionInSignature); + this.getCommunicationTracker().registerDependency(this, + aggregatorOuterIdentityIndexers[resultPositionInSignature]); + } + return aggregatorOuterIdentityIndexers[resultPositionInSignature]; + } + + /** + * @since 2.4 + */ + public void propagateAggregateResultUpdate(final Tuple group, final AggregateResult oldValue, + final AggregateResult newValue, final Timestamp timestamp) { + if (!Objects.equals(oldValue, newValue)) { + propagate(Direction.DELETE, group, oldValue, timestamp); + propagate(Direction.INSERT, group, newValue, timestamp); + } + } + + /** + * @since 2.4 + */ + @SuppressWarnings("unchecked") + public void propagate(final Direction direction, final Tuple group, final AggregateResult value, + final Timestamp timestamp) { + final Tuple tuple = tupleFromAggregateResult(group, value); + + if (aggregatorOuterIndexer != null) { + aggregatorOuterIndexer.propagate(direction, tuple, group, timestamp); + } + if (aggregatorOuterIdentityIndexers != null) { + for (final AggregatorOuterIdentityIndexer aggregatorOuterIdentityIndexer : aggregatorOuterIdentityIndexers) { + if (aggregatorOuterIdentityIndexer != null) { + aggregatorOuterIdentityIndexer.propagate(direction, tuple, group, timestamp); + } + } + } + } + + public abstract Tuple getAggregateTuple(final Tuple key); + + /** + * @since 2.4 + */ + public abstract Map> getAggregateTupleTimeline(final Tuple key); + + public abstract AggregateResult getAggregateResult(final Tuple key); + + /** + * @since 2.4 + */ + public abstract Map> getAggregateResultTimeline(final Tuple key); + + protected Tuple tupleFromAggregateResult(final Tuple groupTuple, final AggregateResult aggregateResult) { + if (aggregateResult == null) { + return null; + } else { + return Tuples.staticArityLeftInheritanceTupleOf(groupTuple, runtimeContext.wrapElement(aggregateResult)); + } + } + + /** + * A special non-iterable index that retrieves the aggregated, packed result (signature+aggregate) for the original + * signature. + * + * @author Gabor Bergmann + * @author Tamas Szabo + * + */ + protected class AggregatorOuterIndexer extends StandardIndexer { + + /** + * @since 2.4 + */ + protected NetworkStructureChangeSensitiveLogic logic; + + public AggregatorOuterIndexer() { + super(AbstractColumnAggregatorNode.this.reteContainer, TupleMask.omit(sourceWidth, sourceWidth + 1)); + this.parent = AbstractColumnAggregatorNode.this; + this.logic = createLogic(); + } + + @Override + public void networkStructureChanged() { + super.networkStructureChanged(); + this.logic = createLogic(); + } + + @Override + public Collection get(final Tuple signature) { + return this.logic.get(signature); + } + + @Override + public Map> getTimeline(final Tuple signature) { + return this.logic.getTimeline(signature); + } + + /** + * @since 2.4 + */ + public void propagate(final Direction direction, final Tuple tuple, final Tuple group, + final Timestamp timestamp) { + if (tuple != null) { + propagate(direction, tuple, group, true, timestamp); + } + } + + @Override + public Node getActiveNode() { + return AbstractColumnAggregatorNode.this; + } + + /** + * @since 2.4 + */ + protected NetworkStructureChangeSensitiveLogic createLogic() { + if (this.reteContainer.isTimelyEvaluation() + && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { + return this.TIMELY; + } else { + return this.TIMELESS; + } + } + + private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { + + @Override + public Collection get(final Tuple signature) { + final Tuple aggregateTuple = getAggregateTuple(signature); + if (aggregateTuple == null) { + return null; + } else { + return Collections.singleton(aggregateTuple); + } + } + + @Override + public Map> getTimeline(final Tuple signature) { + throw new UnsupportedOperationException(); + } + + }; + + private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { + + @Override + public Collection get(final Tuple signatureWithResult) { + return TIMELESS.get(signatureWithResult); + } + + @Override + public Map> getTimeline(final Tuple signature) { + final Map> aggregateTuples = getAggregateTupleTimeline(signature); + if (aggregateTuples.isEmpty()) { + return null; + } else { + return aggregateTuples; + } + } + + }; + + } + + /** + * A special non-iterable index that checks a suspected aggregate value for a given signature. The signature for + * this index is the original 'group by' masked tuple, with the suspected result inserted at position + * resultPositionInSignature. + * + * @author Gabor Bergmann + * @author Tamas Szabo + * + */ + protected class AggregatorOuterIdentityIndexer extends StandardIndexer { + + protected final int resultPositionInSignature; + protected final TupleMask pruneResult; + protected final TupleMask reorderMask; + /** + * @since 2.4 + */ + protected NetworkStructureChangeSensitiveLogic logic; + + public AggregatorOuterIdentityIndexer(final int resultPositionInSignature) { + super(AbstractColumnAggregatorNode.this.reteContainer, + TupleMask.displace(sourceWidth, resultPositionInSignature, sourceWidth + 1)); + this.resultPositionInSignature = resultPositionInSignature; + this.pruneResult = TupleMask.omit(resultPositionInSignature, sourceWidth + 1); + if (resultPositionInSignature == sourceWidth) { + this.reorderMask = null; + } else { + this.reorderMask = mask; + } + this.logic = createLogic(); + } + + @Override + public void networkStructureChanged() { + super.networkStructureChanged(); + this.logic = createLogic(); + } + + @Override + public Collection get(final Tuple signatureWithResult) { + return this.logic.get(signatureWithResult); + } + + /** + * @since 2.4 + */ + @Override + public Map> getTimeline(final Tuple signature) { + return this.logic.getTimeline(signature); + } + + /** + * @since 2.4 + */ + public void propagate(final Direction direction, final Tuple tuple, final Tuple group, + final Timestamp timestamp) { + if (tuple != null) { + propagate(direction, reorder(tuple), group, true, timestamp); + } + } + + private Tuple reorder(final Tuple signatureWithResult) { + Tuple transformed; + if (reorderMask == null) { + transformed = signatureWithResult; + } else { + transformed = reorderMask.transform(signatureWithResult); + } + return transformed; + } + + @Override + public Node getActiveNode() { + return this.parent; + } + + /** + * @since 2.4 + */ + protected NetworkStructureChangeSensitiveLogic createLogic() { + if (this.reteContainer.isTimelyEvaluation() + && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { + return this.TIMELY; + } else { + return this.TIMELESS; + } + } + + private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { + + @Override + public Collection get(final Tuple signatureWithResult) { + final Tuple prunedSignature = pruneResult.transform(signatureWithResult); + final AggregateResult result = getAggregateResult(prunedSignature); + if (result != null && Objects.equals(signatureWithResult.get(resultPositionInSignature), result)) { + return Collections.singleton(signatureWithResult); + } else { + return null; + } + } + + @Override + public Map> getTimeline(final Tuple signature) { + throw new UnsupportedOperationException(); + } + + }; + + private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { + + @Override + public Collection get(final Tuple signatureWithResult) { + return TIMELESS.get(signatureWithResult); + } + + @Override + public Map> getTimeline(final Tuple signatureWithResult) { + final Tuple prunedSignature = pruneResult.transform(signatureWithResult); + final Map> result = getAggregateResultTimeline(prunedSignature); + for (final Entry> entry : result.entrySet()) { + if (Objects.equals(signatureWithResult.get(resultPositionInSignature), entry.getKey())) { + return Collections.singletonMap(signatureWithResult, entry.getValue()); + } + } + return null; + } + + }; + + } + + /** + * @since 2.4 + */ + protected static abstract class NetworkStructureChangeSensitiveLogic { + + public abstract Collection get(final Tuple signatureWithResult); + + public abstract Map> getTimeline(final Tuple signature); + + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java new file mode 100644 index 00000000..4480aed8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/ColumnAggregatorNode.java @@ -0,0 +1,369 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation; + +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver; +import tools.refinery.viatra.runtime.rete.network.RederivableNode; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timeless.RecursiveCommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.PosetAwareMailbox; + +/** + * Timeless implementation of the column aggregator node. + *

+ * The node is capable of operating in the delete and re-derive mode. In this mode, it is also possible to equip the + * node with an {@link IPosetComparator} to identify monotone changes; thus, ensuring that a fix-point can be reached + * during the evaluation. + * + * @author Gabor Bergmann + * @author Tamas Szabo + * @since 1.4 + */ +public class ColumnAggregatorNode + extends AbstractColumnAggregatorNode + implements RederivableNode, PosetAwareReceiver { + + /** + * @since 1.6 + */ + protected final IPosetComparator posetComparator; + + /** + * @since 1.6 + */ + protected final boolean deleteRederiveEvaluation; + + // invariant: neutral values are not stored + /** + * @since 1.6 + */ + protected final Map memory; + /** + * @since 1.6 + */ + protected final Map rederivableMemory; + + /** + * @since 1.7 + */ + protected CommunicationGroup currentGroup; + + /** + * Creates a new column aggregator node. + * + * @param reteContainer + * the RETE container of the node + * @param operator + * the aggregation operator + * @param deleteRederiveEvaluation + * true if the node should run in DRED mode, false otherwise + * @param groupMask + * the mask that masks a tuple to obtain the key that we are grouping-by + * @param columnMask + * the mask that masks a tuple to obtain the tuple element(s) that we are aggregating over + * @param posetComparator + * the poset comparator for the column, if known, otherwise it can be null + * @since 1.6 + */ + public ColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final boolean deleteRederiveEvaluation, final TupleMask groupMask, final TupleMask columnMask, + final IPosetComparator posetComparator) { + super(reteContainer, operator, groupMask, columnMask); + this.memory = CollectionsFactory.createMap(); + this.rederivableMemory = CollectionsFactory.createMap(); + this.deleteRederiveEvaluation = deleteRederiveEvaluation; + this.posetComparator = posetComparator; + // mailbox MUST be instantiated after the fields are all set + this.mailbox = instantiateMailbox(); + } + + /** + * Creates a new column aggregator node. + * + * @param reteContainer + * the RETE container of the node + * @param operator + * the aggregation operator + * @param groupMask + * the mask that masks a tuple to obtain the key that we are grouping-by + * @param aggregatedColumn + * the index of the column that the aggregator node is aggregating over + */ + public ColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final TupleMask groupMask, final int aggregatedColumn) { + this(reteContainer, operator, false, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth), + null); + } + + @Override + public boolean isInDRedMode() { + return this.deleteRederiveEvaluation; + } + + @Override + protected Mailbox instantiateMailbox() { + if (groupMask != null && columnMask != null && posetComparator != null) { + return new PosetAwareMailbox(this, this.reteContainer); + } else { + return new BehaviorChangingMailbox(this, this.reteContainer); + } + } + + @Override + public TupleMask getCoreMask() { + return groupMask; + } + + @Override + public TupleMask getPosetMask() { + return columnMask; + } + + @Override + public IPosetComparator getPosetComparator() { + return posetComparator; + } + + @Override + public void rederiveOne() { + final Entry entry = rederivableMemory.entrySet().iterator().next(); + final Tuple group = entry.getKey(); + final Accumulator accumulator = entry.getValue(); + rederivableMemory.remove(group); + memory.put(group, accumulator); + // unregister the node if there is nothing left to be re-derived + if (this.rederivableMemory.isEmpty()) { + ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); + } + final AggregateResult value = operator.getAggregate(accumulator); + propagateAggregateResultUpdate(group, NEUTRAL, value, Timestamp.ZERO); + } + + @Override + public void updateWithPosetInfo(final Direction direction, final Tuple update, final boolean monotone) { + if (this.deleteRederiveEvaluation) { + updateWithDeleteAndRederive(direction, update, monotone); + } else { + updateDefault(direction, update, Timestamp.ZERO); + } + } + + @Override + public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { + updateWithPosetInfo(direction, update, false); + } + + /** + * @since 2.4 + */ + protected void updateDefault(final Direction direction, final Tuple update, final Timestamp timestamp) { + final Tuple key = groupMask.transform(update); + final Tuple value = columnMask.transform(update); + @SuppressWarnings("unchecked") + final Domain aggregableValue = (Domain) runtimeContext.unwrapElement(value.get(0)); + final boolean isInsertion = direction == Direction.INSERT; + + final Accumulator oldMainAccumulator = getMainAccumulator(key); + final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator); + + final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue, isInsertion); + storeIfNotNeutral(key, newMainAccumulator, memory); + final AggregateResult newValue = operator.getAggregate(newMainAccumulator); + + propagateAggregateResultUpdate(key, oldValue, newValue, timestamp); + } + + /** + * @since 2.4 + */ + protected void updateWithDeleteAndRederive(final Direction direction, final Tuple update, final boolean monotone) { + final Tuple group = groupMask.transform(update); + final Tuple value = columnMask.transform(update); + @SuppressWarnings("unchecked") + final Domain aggregableValue = (Domain) runtimeContext.unwrapElement(value.get(0)); + final boolean isInsertion = direction == Direction.INSERT; + + Accumulator oldMainAccumulator = memory.get(group); + Accumulator oldRederivableAccumulator = rederivableMemory.get(group); + + if (direction == Direction.INSERT) { + // INSERT + if (oldRederivableAccumulator != null) { + // the group is in the re-derivable memory + final Accumulator newRederivableAccumulator = operator.update(oldRederivableAccumulator, + aggregableValue, isInsertion); + storeIfNotNeutral(group, newRederivableAccumulator, rederivableMemory); + if (rederivableMemory.isEmpty()) { + // there is nothing left to be re-derived + // this can happen if the accumulator became neutral in response to the INSERT + ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); + } + } else { + // the group is in the main memory + // at this point, it can happen that we need to initialize with a neutral accumulator + if (oldMainAccumulator == null) { + oldMainAccumulator = operator.createNeutral(); + } + + final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator); + final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue, + isInsertion); + storeIfNotNeutral(group, newMainAccumulator, memory); + final AggregateResult newValue = operator.getAggregate(newMainAccumulator); + propagateAggregateResultUpdate(group, oldValue, newValue, Timestamp.ZERO); + } + } else { + // DELETE + if (oldRederivableAccumulator != null) { + // the group is in the re-derivable memory + if (oldMainAccumulator != null) { + issueError("[INTERNAL ERROR] Inconsistent state for " + update + + " because it is present both in the main and re-derivable memory in the ColumnAggregatorNode " + + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(), null); + } + try { + final Accumulator newRederivableAccumulator = operator.update(oldRederivableAccumulator, + aggregableValue, isInsertion); + storeIfNotNeutral(group, newRederivableAccumulator, rederivableMemory); + if (rederivableMemory.isEmpty()) { + // there is nothing left to be re-derived + // this can happen if the accumulator became neutral in response to the DELETE + ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); + } + } catch (final NullPointerException ex) { + issueError("[INTERNAL ERROR] Deleting a domain element in " + update + + " which did not exist before in ColumnAggregatorNode " + this + " for pattern(s) " + + getTraceInfoPatternsEnumerated(), ex); + } + } else { + // the group is in the main memory + // at this point, it can happen that we need to initialize with a neutral accumulator + if (oldMainAccumulator == null) { + oldMainAccumulator = operator.createNeutral(); + } + + final AggregateResult oldValue = operator.getAggregate(oldMainAccumulator); + final Accumulator newMainAccumulator = operator.update(oldMainAccumulator, aggregableValue, + isInsertion); + final AggregateResult newValue = operator.getAggregate(newMainAccumulator); + + if (monotone) { + storeIfNotNeutral(group, newMainAccumulator, memory); + propagateAggregateResultUpdate(group, oldValue, newValue, Timestamp.ZERO); + } else { + final boolean wasEmpty = rederivableMemory.isEmpty(); + if (storeIfNotNeutral(group, newMainAccumulator, rederivableMemory) && wasEmpty) { + ((RecursiveCommunicationGroup) currentGroup).addRederivable(this); + } + memory.remove(group); + propagateAggregateResultUpdate(group, oldValue, NEUTRAL, Timestamp.ZERO); + } + } + } + } + + @Override + public void clear() { + this.memory.clear(); + this.rederivableMemory.clear(); + this.childMailboxes.clear(); + } + + /** + * Returns true if the accumulator was stored, false otherwise. + * + * @since 1.6 + */ + protected boolean storeIfNotNeutral(final Tuple key, final Accumulator accumulator, + final Map memory) { + if (operator.isNeutral(accumulator)) { + memory.remove(key); + return false; + } else { + memory.put(key, accumulator); + return true; + } + } + + @Override + public Tuple getAggregateTuple(final Tuple group) { + final Accumulator accumulator = getMainAccumulator(group); + final AggregateResult result = operator.getAggregate(accumulator); + return tupleFromAggregateResult(group, result); + } + + @Override + public AggregateResult getAggregateResult(final Tuple group) { + final Accumulator accumulator = getMainAccumulator(group); + return operator.getAggregate(accumulator); + } + + @Override + public Map> getAggregateResultTimeline(Tuple key) { + throw new UnsupportedOperationException(); + } + + @Override + public Map> getAggregateTupleTimeline(Tuple key) { + throw new UnsupportedOperationException(); + } + + /** + * @since 1.6 + */ + protected Accumulator getMainAccumulator(final Tuple key) { + return getAccumulator(key, memory); + } + + /** + * @since 1.6 + */ + protected Accumulator getRederivableAccumulator(final Tuple key) { + return getAccumulator(key, rederivableMemory); + } + + /** + * @since 1.6 + */ + protected Accumulator getAccumulator(final Tuple key, final Map memory) { + Accumulator accumulator = memory.get(key); + if (accumulator == null) { + return operator.createNeutral(); + } else { + return accumulator; + } + } + + @Override + public CommunicationGroup getCurrentGroup() { + return currentGroup; + } + + @Override + public void setCurrentGroup(final CommunicationGroup currentGroup) { + this.currentGroup = currentGroup; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java new file mode 100644 index 00000000..7c98de0d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/CountNode.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.aggregation; + +import java.util.Collection; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; + +/** + * An aggregation node that simply counts the number of tuples conforming to the signature. + * + * @author Gabor Bergmann + * @since 1.4 + */ +public class CountNode extends IndexerBasedAggregatorNode { + + public CountNode(ReteContainer reteContainer) { + super(reteContainer); + } + + int sizeOf(Collection group) { + return group == null ? 0 : group.size(); + } + + @Override + public Object aggregateGroup(Tuple signature, Collection group) { + return sizeOf(group); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java new file mode 100644 index 00000000..3c7850de --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedMap.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation; + +import java.util.AbstractMap.SimpleEntry; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; + +/** + * An optimized {@link Map} implementation where each key is produced by joining together a group tuple and some other + * object (via left inheritance). Only a select few {@link Map} operations are supported. This collection is + * unmodifiable. + * + * Operations on this map assume that client queries also obey the contract that keys are constructed from a group tuple + * and an additional object. + * + * @author Tamas Szabo + * @since 2.4 + */ +public class GroupedMap implements Map { + + protected final Tuple group; + // cached group size value is to be used in get() + private final int groupSize; + protected final Map mappings; + protected final IQueryRuntimeContext runtimeContext; + + public GroupedMap(final Tuple group, final Map mappings, + final IQueryRuntimeContext runtimeContext) { + this.group = group; + this.groupSize = group.getSize(); + this.mappings = mappings; + this.runtimeContext = runtimeContext; + } + + @Override + public int size() { + return this.mappings.size(); + } + + @Override + public boolean isEmpty() { + return this.mappings.isEmpty(); + } + + @Override + public boolean containsKey(final Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsValue(final Object value) { + return this.mappings.containsValue(value); + } + + @Override + public ValueType get(final Object key) { + if (key instanceof Tuple) { + final Object value = ((Tuple) key).get(this.groupSize); + final Object unwrappedValue = this.runtimeContext.unwrapElement(value); + return this.mappings.get(unwrappedValue); + } else { + return null; + } + } + + @Override + public ValueType put(final Tuple key, final ValueType value) { + throw new UnsupportedOperationException(); + } + + @Override + public ValueType remove(final Object key) { + throw new UnsupportedOperationException(); + } + + @Override + public void putAll(final Map map) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public Set keySet() { + return new GroupedSet(this.group, this.mappings.keySet(), (g, v) -> { + return Tuples.staticArityLeftInheritanceTupleOf(g, this.runtimeContext.wrapElement(v)); + }); + } + + @Override + public Collection values() { + return this.mappings.values(); + } + + @Override + public Set> entrySet() { + return new GroupedSet>(this.group, this.mappings.keySet(), + (g, v) -> { + final Tuple key = Tuples.staticArityLeftInheritanceTupleOf(g, this.runtimeContext.wrapElement(v)); + final ValueType value = this.mappings.get(v); + return new SimpleEntry(key, value); + }); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java new file mode 100644 index 00000000..65561e53 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/GroupedSet.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; +import java.util.function.BiFunction; + +/** + * An optimized {@link Set} implementation where each contained value is produced by combining together a grouping value + * and some other (key) object. The way of combining together these two values is specified by the closure passed to the + * constructor. Only a select few {@link Set} operations are supported. This collection is unmodifiable. + * + * @author Tamas Szabo + * @since 2.4 + */ +public class GroupedSet implements Set { + + protected final GroupingValueType group; + protected final Collection values; + protected final BiFunction valueFunc; + + public GroupedSet(final GroupingValueType group, final Collection values, + final BiFunction valueFunc) { + this.group = group; + this.values = values; + this.valueFunc = valueFunc; + } + + @Override + public int size() { + return this.values.size(); + } + + @Override + public boolean isEmpty() { + return this.values.isEmpty(); + } + + @Override + public boolean contains(final Object obj) { + throw new UnsupportedOperationException(); + } + + @Override + public Iterator iterator() { + final Iterator wrapped = this.values.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return wrapped.hasNext(); + } + + @Override + public WholeKeyType next() { + final GroupedKeyType value = wrapped.next(); + return valueFunc.apply(group, value); + } + }; + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @Override + public T[] toArray(final T[] arr) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean add(final WholeKeyType tuple) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(final Object obj) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean containsAll(final Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(final Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java new file mode 100644 index 00000000..6c286364 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IAggregatorNode.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation; + +import tools.refinery.viatra.runtime.rete.index.Indexer; + +/** + * Expresses that aggregators expose specialized non-enumerable indexers for outer joining. + * @author Gabor Bergmann + * + * @since 1.4 + * + */ +public interface IAggregatorNode { + + Indexer getAggregatorOuterIndexer(); + + Indexer getAggregatorOuterIdentityIndexer(int resultPositionInSignature); + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java new file mode 100644 index 00000000..d9a94a82 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/IndexerBasedAggregatorNode.java @@ -0,0 +1,278 @@ +/******************************************************************************* + * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.aggregation; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.index.DefaultIndexerListener; +import tools.refinery.viatra.runtime.rete.index.Indexer; +import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; +import tools.refinery.viatra.runtime.rete.index.StandardIndexer; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.StandardNode; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; + +/** + * A special node depending on a projection indexer to aggregate tuple groups with the same projection. Only propagates + * the aggregates of non-empty groups. Use the outer indexers to circumvent. + *

+ * This node cannot be used in recursive differential dataflow evaluation. + * + * @author Gabor Bergmann + * @since 1.4 + */ +public abstract class IndexerBasedAggregatorNode extends StandardNode implements IAggregatorNode { + + ProjectionIndexer projection; + IndexerBasedAggregatorNode me; + int sourceWidth; + Map mainAggregates; + + AggregatorOuterIndexer aggregatorOuterIndexer = null; + AggregatorOuterIdentityIndexer[] aggregatorOuterIdentityIndexers = null; + + /** + * MUST call initializeWith() afterwards! + */ + public IndexerBasedAggregatorNode(ReteContainer reteContainer) { + super(reteContainer); + this.me = this; + mainAggregates = CollectionsFactory.createMap(); + } + + @Override + public void networkStructureChanged() { + if (this.reteContainer.isTimelyEvaluation() && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { + throw new IllegalStateException(this.toString() + " cannot be used in recursive differential dataflow evaluation!"); + } + super.networkStructureChanged(); + } + + /** + * @param projection + * the projection indexer whose tuple groups should be aggregated + */ + public void initializeWith(ProjectionIndexer projection) { + this.projection = projection; + this.sourceWidth = projection.getMask().indices.length; + + for (Tuple signature : projection.getSignatures()) { + mainAggregates.put(signature, aggregateGroup(signature, projection.get(signature))); + } + projection.attachListener(new DefaultIndexerListener(this) { + @Override + public void notifyIndexerUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp) { + aggregateUpdate(direction, updateElement, signature, change); + } + }); + } + + /** + * Aggregates (reduces) a group of tuples. The group can be null. + */ + public abstract Object aggregateGroup(Tuple signature, Collection group); + + + /** + * Aggregates (reduces) a group of tuples, having access to the previous aggregated value (before the update) and + * the update definition. Defaults to aggregateGroup(). Override to increase performance. + * @since 2.4 + */ + public Object aggregateGroupAfterUpdate(Tuple signature, Collection currentGroup, Object oldAggregate, + Direction direction, Tuple updateElement, boolean change) { + return aggregateGroup(signature, currentGroup); + } + + protected Tuple aggregateAndPack(Tuple signature, Collection group) { + return packResult(signature, aggregateGroup(signature, group)); + } + + @Override + public Indexer getAggregatorOuterIndexer() { + if (aggregatorOuterIndexer == null) { + aggregatorOuterIndexer = new AggregatorOuterIndexer(); + this.getCommunicationTracker().registerDependency(this, aggregatorOuterIndexer); + // reteContainer.connectAndSynchronize(this, aggregatorOuterIndexer); + } + return aggregatorOuterIndexer; + } + + @Override + public Indexer getAggregatorOuterIdentityIndexer(int resultPositionInSignature) { + if (aggregatorOuterIdentityIndexers == null) + aggregatorOuterIdentityIndexers = new AggregatorOuterIdentityIndexer[sourceWidth + 1]; + if (aggregatorOuterIdentityIndexers[resultPositionInSignature] == null) { + aggregatorOuterIdentityIndexers[resultPositionInSignature] = new AggregatorOuterIdentityIndexer( + resultPositionInSignature); + this.getCommunicationTracker().registerDependency(this, aggregatorOuterIdentityIndexers[resultPositionInSignature]); + // reteContainer.connectAndSynchronize(this, aggregatorOuterIdentityIndexers[resultPositionInSignature]); + } + return aggregatorOuterIdentityIndexers[resultPositionInSignature]; + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + for (final Entry aggregateEntry : mainAggregates.entrySet()) { + collector.add(packResult(aggregateEntry.getKey(), aggregateEntry.getValue())); + } + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + // use all zero timestamps because this node cannot be used in recursive groups anyway + for (final Entry aggregateEntry : mainAggregates.entrySet()) { + collector.put(packResult(aggregateEntry.getKey(), aggregateEntry.getValue()), Timestamp.INSERT_AT_ZERO_TIMELINE); + } + } + + protected Tuple packResult(Tuple signature, Object result) { + return Tuples.staticArityLeftInheritanceTupleOf(signature, result); + } + + /** + * @since 2.4 + */ + protected void aggregateUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change) { + Collection currentGroup = projection.get(signature); + // these will be null if group is empty + Object oldAggregate = mainAggregates.get(signature); + Object safeOldAggregate = oldAggregate == null ? aggregateGroup(signature, null) : oldAggregate; + boolean empty = currentGroup == null || currentGroup.isEmpty(); + Object newAggregate = empty ? null : aggregateGroupAfterUpdate(signature, currentGroup, safeOldAggregate/* + * non-null + */, + direction, updateElement, change); + if (!empty) + mainAggregates.put(signature, newAggregate); + else + mainAggregates.remove(signature); + Tuple oldTuple = packResult(signature, safeOldAggregate); + Tuple newTuple = packResult(signature, newAggregate == null ? aggregateGroup(signature, null) : newAggregate); + if (oldAggregate != null) + propagateUpdate(Direction.DELETE, oldTuple, Timestamp.ZERO); // direct outputs lack non-empty groups + if (newAggregate != null) + propagateUpdate(Direction.INSERT, newTuple, Timestamp.ZERO); // direct outputs lack non-empty groups + if (aggregatorOuterIndexer != null) + aggregatorOuterIndexer.propagate(signature, oldTuple, newTuple); + if (aggregatorOuterIdentityIndexers != null) + for (AggregatorOuterIdentityIndexer aggregatorOuterIdentityIndexer : aggregatorOuterIdentityIndexers) + if (aggregatorOuterIdentityIndexer != null) + aggregatorOuterIdentityIndexer.propagate(signature, oldTuple, newTuple); + } + + private Object getAggregate(Tuple signature) { + Object aggregate = mainAggregates.get(signature); + return aggregate == null ? aggregateGroup(signature, null) : aggregate; + } + + @Override + public void assignTraceInfo(TraceInfo traceInfo) { + super.assignTraceInfo(traceInfo); + if (traceInfo.propagateToIndexerParent() && projection != null) + projection.acceptPropagatedTraceInfo(traceInfo); + } + + /** + * A special non-iterable index that retrieves the aggregated, packed result (signature+aggregate) for the original + * signature. + * + * @author Gabor Bergmann + */ + class AggregatorOuterIndexer extends StandardIndexer { + + public AggregatorOuterIndexer() { + super(me.reteContainer, TupleMask.omit(sourceWidth, sourceWidth + 1)); + this.parent = me; + } + + @Override + public Collection get(Tuple signature) { + return Collections.singleton(packResult(signature, getAggregate(signature))); + } + + public void propagate(Tuple signature, Tuple oldTuple, Tuple newTuple) { + propagate(Direction.INSERT, newTuple, signature, false, Timestamp.ZERO); + propagate(Direction.DELETE, oldTuple, signature, false, Timestamp.ZERO); + } + + @Override + public Node getActiveNode() { + return projection.getActiveNode(); + } + + } + + /** + * A special non-iterable index that checks a suspected aggregate value for a given signature. The signature for + * this index is the original signature of the projection index, with the suspected result inserted at position + * resultPositionInSignature. + * + * @author Gabor Bergmann + */ + + class AggregatorOuterIdentityIndexer extends StandardIndexer { + int resultPositionInSignature; + TupleMask pruneResult; + TupleMask reorderMask; + + public AggregatorOuterIdentityIndexer(int resultPositionInSignature) { + super(me.reteContainer, TupleMask.displace(sourceWidth, resultPositionInSignature, sourceWidth + 1)); + this.parent = me; + // this.localAggregates = new HashMap(); + this.resultPositionInSignature = resultPositionInSignature; + this.pruneResult = TupleMask.omit(resultPositionInSignature, sourceWidth + 1); + if (resultPositionInSignature == sourceWidth) + this.reorderMask = null; + else + this.reorderMask = mask; + } + + @Override + public Collection get(Tuple signatureWithResult) { + Tuple prunedSignature = pruneResult.transform(signatureWithResult); + Object result = getAggregate(prunedSignature); + if (signatureWithResult.get(resultPositionInSignature).equals(result)) + return Collections.singleton(signatureWithResult); + else + return null; + } + + public void propagate(Tuple signature, Tuple oldTuple, Tuple newTuple) { + propagate(Direction.INSERT, reorder(newTuple), signature, true, Timestamp.ZERO); + propagate(Direction.DELETE, reorder(oldTuple), signature, true, Timestamp.ZERO); + } + + private Tuple reorder(Tuple signatureWithResult) { + Tuple transformed; + if (reorderMask == null) + transformed = signatureWithResult; + else + transformed = reorderMask.transform(signatureWithResult); + return transformed; + } + + @Override + public Node getActiveNode() { + return projection.getActiveNode(); + } + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..a9863400 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation.timely; + +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.TreeMap; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.IDeltaBag; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode.CumulativeAggregate; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode.FoldingState; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulTimelyColumnAggregatorNode.MergeableFoldingState; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; + +/** + * Faithful column aggregator with parallel aggregation architecture. + * + * @author Tamas Szabo + * @since 2.4 + * + */ +public class FaithfulParallelTimelyColumnAggregatorNode extends + FaithfulTimelyColumnAggregatorNode, FoldingState> + implements ResumableNode { + + public FaithfulParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final TupleMask groupMask, final TupleMask columnMask) { + super(reteContainer, operator, groupMask, columnMask); + } + + public FaithfulParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final TupleMask groupMask, final int aggregatedColumn) { + this(reteContainer, operator, groupMask, TupleMask.selectSingle(aggregatedColumn, groupMask.sourceWidth)); + } + + @Override + protected Map> doFoldingStep(final Tuple group, final FoldingState state, + final Timestamp timestamp) { + final CumulativeAggregate aggregate = getAggregate(group, timestamp); + if (state.delta.isEmpty()) { + gcAggregates(aggregate, group, timestamp); + return Collections.emptyMap(); + } else { + final Map> diffMap = CollectionsFactory.createMap(); + final Timestamp nextTimestamp = this.aggregates.get(group).higherKey(timestamp); + + final AggregateResult currentOldResult = operator.getAggregate(aggregate.accumulator); + + for (final Entry entry : state.delta.entriesWithMultiplicities()) { + final boolean isInsertion = entry.getValue() > 0; + final Domain aggregand = entry.getKey(); + for (int i = 0; i < Math.abs(entry.getValue()); i++) { + aggregate.accumulator = operator.update(aggregate.accumulator, aggregand, isInsertion); + } + } + + final AggregateResult currentNewResult = operator.getAggregate(aggregate.accumulator); + + if (!Objects.equals(currentOldResult, currentNewResult)) { + // current old result disappears here + appendDiff(currentOldResult, new Signed<>(Direction.DELETE, timestamp), diffMap); + if (nextTimestamp != null) { + appendDiff(currentOldResult, new Signed<>(Direction.INSERT, nextTimestamp), diffMap); + } + + // current new result appears here + appendDiff(currentNewResult, new Signed<>(Direction.INSERT, timestamp), diffMap); + if (nextTimestamp != null) { + appendDiff(currentNewResult, new Signed<>(Direction.DELETE, nextTimestamp), diffMap); + } + } + + gcAggregates(aggregate, group, timestamp); + updateTimeline(group, diffMap); + + // prepare folding state for next timestamp + if (nextTimestamp != null) { + final FoldingState newState = new FoldingState<>(); + newState.delta = state.delta; + addFoldingState(group, newState, nextTimestamp); + } + + return diffMap; + } + } + + @Override + public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { + final Tuple group = groupMask.transform(update); + final Tuple value = columnMask.transform(update); + @SuppressWarnings("unchecked") + final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0)); + final boolean isInsertion = direction == Direction.INSERT; + + final CumulativeAggregate aggregate = getAggregate(group, timestamp); + final FoldingState state = new FoldingState<>(); + if (isInsertion) { + aggregate.aggregands.addOne(aggregand); + state.delta.addOne(aggregand); + } else { + aggregate.aggregands.removeOne(aggregand); + state.delta.removeOne(aggregand); + } + + addFoldingState(group, state, timestamp); + } + + /** + * Garbage collects the counter of the given group and timestamp if the bag of aggregands is empty. + */ + @Override + protected void gcAggregates(final CumulativeAggregate aggregate, final Tuple group, + final Timestamp timestamp) { + if (aggregate.aggregands.isEmpty()) { + final TreeMap> groupAggregates = this.aggregates + .get(group); + groupAggregates.remove(timestamp); + if (groupAggregates.isEmpty()) { + this.aggregates.remove(group); + } + } + } + + /** + * On-demand initializes and returns the aggregate for the given group and timestamp. + */ + @Override + protected CumulativeAggregate getAggregate(final Tuple group, final Timestamp timestamp) { + final TreeMap> groupAggregates = this.aggregates + .computeIfAbsent(group, k -> CollectionsFactory.createTreeMap()); + return groupAggregates.computeIfAbsent(timestamp, k -> { + final CumulativeAggregate aggregate = new CumulativeAggregate<>(); + final Entry> lowerEntry = groupAggregates + .lowerEntry(timestamp); + if (lowerEntry == null) { + aggregate.accumulator = operator.createNeutral(); + } else { + aggregate.accumulator = operator.clone(lowerEntry.getValue().accumulator); + } + return aggregate; + }); + } + + @Override + public AggregateResult getAggregateResult(final Tuple group) { + final TreeMap> groupAggregates = this.aggregates.get(group); + if (groupAggregates != null) { + final Entry> lastEntry = groupAggregates.lastEntry(); + return operator.getAggregate(lastEntry.getValue().accumulator); + } else { + return NEUTRAL; + } + } + + protected static class CumulativeAggregate { + protected Accumulator accumulator; + protected IDeltaBag aggregands; + + protected CumulativeAggregate() { + this.aggregands = CollectionsFactory.createDeltaBag(); + } + + @Override + public String toString() { + return "accumulator=" + accumulator + " aggregands=" + aggregands; + } + } + + protected static class FoldingState implements MergeableFoldingState> { + protected IDeltaBag delta; + + protected FoldingState() { + this.delta = CollectionsFactory.createDeltaBag(); + } + + @Override + public String toString() { + return "delta=" + delta; + } + + /** + * The returned result will never be null, even if the resulting delta set is empty. + */ + @Override + public FoldingState merge(final FoldingState that) { + Preconditions.checkArgument(that != null); + // 'this' was the previously registered folding state + // 'that' is the new folding state being pushed upwards + final FoldingState result = new FoldingState<>(); + this.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m)); + that.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m)); + return result; + } + + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..666b2051 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulSequentialTimelyColumnAggregatorNode.java @@ -0,0 +1,280 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation.timely; + +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.TreeMap; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.IDeltaBag; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode.CumulativeAggregate; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode.FoldingState; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulTimelyColumnAggregatorNode.MergeableFoldingState; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; + +/** + * Faithful column aggregator with sequential aggregation architecture. + * + * @author Tamas Szabo + * @since 2.4 + * + */ +public class FaithfulSequentialTimelyColumnAggregatorNode extends + FaithfulTimelyColumnAggregatorNode, FoldingState> + implements ResumableNode { + + protected boolean isRecursiveAggregation; + + public FaithfulSequentialTimelyColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final TupleMask groupMask, final TupleMask columnMask) { + super(reteContainer, operator, groupMask, columnMask); + this.isRecursiveAggregation = false; + } + + @Override + public void networkStructureChanged() { + super.networkStructureChanged(); + this.isRecursiveAggregation = this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this); + } + + @Override + protected Map> doFoldingStep(final Tuple group, + final FoldingState state, final Timestamp timestamp) { + final CumulativeAggregate aggregate = getAggregate(group, timestamp); + if (state.delta.isEmpty() && Objects.equals(state.oldResult, state.newResult)) { + gcAggregates(aggregate, group, timestamp); + return Collections.emptyMap(); + } else { + final Map> diffMap = CollectionsFactory.createMap(); + final Timestamp nextTimestamp = this.aggregates.get(group).higherKey(timestamp); + + final AggregateResult previousOldResult = state.oldResult; + final AggregateResult previousNewResult = state.newResult; + + final AggregateResult currentOldResult = previousOldResult == null + ? operator.getAggregate(aggregate.positive) + : operator.combine(previousOldResult, aggregate.positive); + + for (final Entry entry : state.delta.entriesWithMultiplicities()) { + final boolean isInsertion = entry.getValue() > 0; + final Domain aggregand = entry.getKey(); + if (isInsertion) { + for (int i = 0; i < entry.getValue(); i++) { + if (isRecursiveAggregation) { + final boolean contains = aggregate.negative.containsNonZero(aggregand); + if (contains) { + aggregate.negative.addOne(aggregand); + } else { + aggregate.positive = operator.update(aggregate.positive, aggregand, true); + } + } else { + aggregate.positive = operator.update(aggregate.positive, aggregand, true); + } + } + } else { + for (int i = 0; i < -entry.getValue(); i++) { + if (isRecursiveAggregation) { + final boolean contains = operator.contains(aggregand, aggregate.positive); + if (contains) { + aggregate.positive = operator.update(aggregate.positive, aggregand, false); + } else { + aggregate.negative.removeOne(aggregand); + } + } else { + aggregate.positive = operator.update(aggregate.positive, aggregand, false); + } + } + } + } + + final AggregateResult currentNewResult = previousNewResult == null + ? operator.getAggregate(aggregate.positive) + : operator.combine(previousNewResult, aggregate.positive); + + aggregate.cachedResult = currentNewResult; + + final boolean sameResult = Objects.equals(currentOldResult, currentNewResult); + if (!sameResult) { + // current old result disappears here + appendDiff(currentOldResult, new Signed<>(Direction.DELETE, timestamp), diffMap); + if (nextTimestamp != null) { + appendDiff(currentOldResult, new Signed<>(Direction.INSERT, nextTimestamp), diffMap); + } + + // current new result appears here + appendDiff(currentNewResult, new Signed<>(Direction.INSERT, timestamp), diffMap); + if (nextTimestamp != null) { + appendDiff(currentNewResult, new Signed<>(Direction.DELETE, nextTimestamp), diffMap); + } + } + + gcAggregates(aggregate, group, timestamp); + updateTimeline(group, diffMap); + + // prepare folding state for next timestamp + if (nextTimestamp != null && !sameResult) { + final FoldingState newState = new FoldingState<>(); + // DO NOT push forward the delta in the folding state!!! that one only affects the input timestamp + newState.oldResult = currentOldResult; + newState.newResult = currentNewResult; + addFoldingState(group, newState, nextTimestamp); + } + + return diffMap; + } + } + + @Override + public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { + final Tuple group = groupMask.transform(update); + final Tuple value = columnMask.transform(update); + @SuppressWarnings("unchecked") + final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0)); + final boolean isInsertion = direction == Direction.INSERT; + + final AggregateResult previousResult = getResultRaw(group, timestamp, true); + final FoldingState state = new FoldingState(); + if (isInsertion) { + state.delta.addOne(aggregand); + } else { + state.delta.removeOne(aggregand); + } + state.oldResult = previousResult; + state.newResult = previousResult; + + // it is acceptable if both oldResult and newResult are null at this point + // in that case we did not have a previous entry at a lower timestamp + + addFoldingState(group, state, timestamp); + } + + protected AggregateResult getResultRaw(final Tuple group, final Timestamp timestamp, final boolean lower) { + final TreeMap> entryMap = this.aggregates + .get(group); + if (entryMap == null) { + return null; + } else { + CumulativeAggregate aggregate = null; + if (lower) { + final Entry> lowerEntry = entryMap + .lowerEntry(timestamp); + if (lowerEntry != null) { + aggregate = lowerEntry.getValue(); + } + } else { + aggregate = entryMap.get(timestamp); + } + if (aggregate == null) { + return null; + } else { + return aggregate.cachedResult; + } + } + } + + @Override + protected void gcAggregates(final CumulativeAggregate aggregate, + final Tuple group, final Timestamp timestamp) { + if (operator.isNeutral(aggregate.positive) && aggregate.negative.isEmpty()) { + final TreeMap> groupAggregates = this.aggregates + .get(group); + groupAggregates.remove(timestamp); + if (groupAggregates.isEmpty()) { + this.aggregates.remove(group); + } + } + } + + @Override + protected CumulativeAggregate getAggregate(final Tuple group, + final Timestamp timestamp) { + final TreeMap> groupAggregates = this.aggregates + .computeIfAbsent(group, k -> CollectionsFactory.createTreeMap()); + return groupAggregates.computeIfAbsent(timestamp, k -> { + final CumulativeAggregate aggregate = new CumulativeAggregate<>(); + aggregate.positive = operator.createNeutral(); + return aggregate; + }); + } + + @Override + public AggregateResult getAggregateResult(final Tuple group) { + final TreeMap> groupAggregates = this.aggregates + .get(group); + if (groupAggregates != null) { + final Entry> lastEntry = groupAggregates + .lastEntry(); + return lastEntry.getValue().cachedResult; + } else { + return NEUTRAL; + } + } + + protected static class CumulativeAggregate { + protected Accumulator positive; + protected IDeltaBag negative; + protected AggregateResult cachedResult; + + protected CumulativeAggregate() { + this.negative = CollectionsFactory.createDeltaBag(); + } + + @Override + public String toString() { + return "positive=" + positive + " negative=" + negative + " cachedResult=" + cachedResult; + } + } + + protected static class FoldingState + implements MergeableFoldingState> { + protected IDeltaBag delta; + protected AggregateResult oldResult; + protected AggregateResult newResult; + + protected FoldingState() { + this.delta = CollectionsFactory.createDeltaBag(); + } + + @Override + public String toString() { + return "delta=" + delta + " oldResult=" + oldResult + " newResult=" + newResult; + } + + /** + * The returned result will never be null, even if the resulting delta set is empty. + */ + @Override + public FoldingState merge(final FoldingState that) { + Preconditions.checkArgument(that != null); + // 'this' was the previously registered folding state + // 'that' is the new folding state being pushed upwards + final FoldingState result = new FoldingState(); + this.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m)); + that.delta.forEachEntryWithMultiplicities((d, m) -> result.delta.addSigned(d, m)); + result.oldResult = this.oldResult; + result.newResult = that.newResult; + return result; + } + + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..8fe9a4e9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulTimelyColumnAggregatorNode.java @@ -0,0 +1,247 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation.timely; + +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.TreeMap; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; +import tools.refinery.viatra.runtime.rete.aggregation.AbstractColumnAggregatorNode; +import tools.refinery.viatra.runtime.rete.aggregation.GroupedMap; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulTimelyColumnAggregatorNode.MergeableFoldingState; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; + +/** + * Faithful timely implementation of the column aggregator node. Complete timelines (series of appearance & + * disappearance) are maintained for tuples.
+ *
+ * Subclasses are responsible for implementing the aggregator architecture, and they must use the CumulativeAggregate + * type parameter for that.
+ *
+ * This node supports recursive aggregation. + * + * @author Tamas Szabo + * @since 2.4 + */ +public abstract class FaithfulTimelyColumnAggregatorNode> + extends AbstractColumnAggregatorNode implements ResumableNode { + + protected final Map> aggregates; + protected final Map>> timelines; + protected final TreeMap> foldingState; + protected CommunicationGroup communicationGroup; + + public FaithfulTimelyColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final TupleMask groupMask, final TupleMask columnMask) { + super(reteContainer, operator, groupMask, columnMask); + this.aggregates = CollectionsFactory.createMap(); + this.timelines = CollectionsFactory.createMap(); + this.foldingState = CollectionsFactory.createTreeMap(); + // mailbox MUST be instantiated after the fields are all set + this.mailbox = instantiateMailbox(); + } + + @Override + protected Mailbox instantiateMailbox() { + return new TimelyMailbox(this, this.reteContainer); + } + + @Override + public void clear() { + this.mailbox.clear(); + this.aggregates.clear(); + this.timelines.clear(); + this.children.clear(); + this.childMailboxes.clear(); + this.foldingState.clear(); + } + + /** + * Registers the given folding state for the specified timestamp and tuple. If there is already a state stored, the + * two states will be merged together. + * + * + */ + protected void addFoldingState(final Tuple group, final FoldingState state, final Timestamp timestamp) { + // assert !state.delta.isEmpty(); + final Map tupleMap = this.foldingState.computeIfAbsent(timestamp, + k -> CollectionsFactory.createMap()); + tupleMap.compute(group, (k, v) -> { + return v == null ? state : v.merge(state); + }); + } + + @Override + public Timestamp getResumableTimestamp() { + if (this.foldingState.isEmpty()) { + return null; + } else { + return this.foldingState.firstKey(); + } + } + + @Override + public void resumeAt(final Timestamp timestamp) { + Timestamp current = this.getResumableTimestamp(); + if (current == null) { + throw new IllegalStateException("There is nothing to fold!"); + } else if (current.compareTo(timestamp) != 0) { + throw new IllegalStateException("Expected to continue folding at " + timestamp + "!"); + } + + final Map tupleMap = this.foldingState.remove(timestamp); + for (final Entry groupEntry : tupleMap.entrySet()) { + final Tuple group = groupEntry.getKey(); + final FoldingState value = groupEntry.getValue(); + final Map> diffMap = doFoldingStep(group, value, timestamp); + for (final Entry> resultEntry : diffMap.entrySet()) { + for (final Signed signed : resultEntry.getValue()) { + propagate(signed.getDirection(), group, resultEntry.getKey(), signed.getPayload()); + } + } + } + + final Timestamp nextTimestamp = this.getResumableTimestamp(); + if (Objects.equals(timestamp, nextTimestamp)) { + throw new IllegalStateException( + "Folding at " + timestamp + " produced more folding work at the same timestamp!"); + } else if (nextTimestamp != null) { + this.communicationGroup.notifyHasMessage(this.mailbox, nextTimestamp); + } + } + + protected abstract Map> doFoldingStep(final Tuple group, final FoldingState state, + final Timestamp timestamp); + + /** + * Updates and garbage collects the timeline of the given tuple based on the given diffs. + */ + protected void updateTimeline(final Tuple group, final Map> diffs) { + if (!diffs.isEmpty()) { + this.timelines.compute(group, (k, resultTimelines) -> { + if (resultTimelines == null) { + resultTimelines = CollectionsFactory.createMap(); + } + for (final Entry> entry : diffs.entrySet()) { + final AggregateResult result = entry.getKey(); + resultTimelines.compute(result, (k2, oldResultTimeline) -> { + final Diff currentResultDiffs = entry.getValue(); + if (oldResultTimeline == null) { + oldResultTimeline = getInitialTimeline(result); + } + final Timeline timeline = oldResultTimeline.mergeAdditive(currentResultDiffs); + if (timeline.isEmpty()) { + return null; + } else { + return timeline; + } + }); + } + if (resultTimelines.isEmpty()) { + return null; + } else { + return resultTimelines; + } + }); + } + } + + /** + * Garbage collects the counter of the given group and timestamp if the bag of aggregands is empty. + */ + protected abstract void gcAggregates(final CumulativeAggregate aggregate, final Tuple group, + final Timestamp timestamp); + + /** + * On-demand initializes and returns the aggregate for the given group and timestamp. + */ + protected abstract CumulativeAggregate getAggregate(final Tuple group, final Timestamp timestamp); + + protected static final Timeline NEUTRAL_INITIAL_TIMELINE = Timestamp.INSERT_AT_ZERO_TIMELINE; + protected static final Timeline NON_NEUTRAL_INITIAL_TIMELINE = Timelines.createEmpty(); + + protected Timeline getInitialTimeline(final AggregateResult result) { + if (NEUTRAL == result) { + return NEUTRAL_INITIAL_TIMELINE; + } else { + return NON_NEUTRAL_INITIAL_TIMELINE; + } + } + + protected static void appendDiff(final AggregateResult result, final Signed diff, + final Map> diffs) { + if (result != null) { + diffs.compute(result, (k, timeLineDiff) -> { + if (timeLineDiff == null) { + timeLineDiff = new Diff<>(); + } + timeLineDiff.add(diff); + return timeLineDiff; + }); + } + } + + @Override + public Tuple getAggregateTuple(final Tuple group) { + return tupleFromAggregateResult(group, getAggregateResult(group)); + } + + @Override + public Map> getAggregateResultTimeline(final Tuple group) { + final Map> resultTimelines = this.timelines.get(group); + if (resultTimelines == null) { + if (NEUTRAL == null) { + return Collections.emptyMap(); + } else { + return Collections.singletonMap(NEUTRAL, NEUTRAL_INITIAL_TIMELINE); + } + } else { + return resultTimelines; + } + } + + @Override + public Map> getAggregateTupleTimeline(final Tuple group) { + final Map> resultTimelines = getAggregateResultTimeline(group); + return new GroupedMap>(group, resultTimelines, this.runtimeContext); + } + + @Override + public CommunicationGroup getCurrentGroup() { + return communicationGroup; + } + + @Override + public void setCurrentGroup(final CommunicationGroup currentGroup) { + this.communicationGroup = currentGroup; + } + + protected interface MergeableFoldingState { + + public abstract T merge(final T that); + + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..733d2585 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyParallelTimelyColumnAggregatorNode.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation.timely; + +import java.util.Map.Entry; +import java.util.TreeMap; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * First-only column aggregator with parallel aggregation architecture. + * + * @author Tamas Szabo + * @since 2.4 + */ +public class FirstOnlyParallelTimelyColumnAggregatorNode + extends FirstOnlyTimelyColumnAggregatorNode { + + public FirstOnlyParallelTimelyColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final TupleMask groupMask, final TupleMask columnMask) { + super(reteContainer, operator, groupMask, columnMask); + } + + /** + * Accumulator gets modified at the input timestamp and at all higher timestamps. Folding cannot be interrupted if + * the new aggregate result is the same as the old at an intermediate timestamp because aggregands need to be copied + * over to all accumulators at the higher timestamps. + */ + @Override + public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { + final Tuple group = groupMask.transform(update); + final Tuple value = columnMask.transform(update); + @SuppressWarnings("unchecked") + final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0)); + final boolean isInsertion = direction == Direction.INSERT; + + final AggregateResult previousResult = getResultRaw(group, timestamp, true); + + Accumulator oldAccumulator = getAccumulator(group, timestamp); + AggregateResult oldResult = operator.getAggregate(oldAccumulator); + + Accumulator newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion); + AggregateResult newResult = operator.getAggregate(newAccumulator); + + storeIfNotNeutral(group, newAccumulator, newResult, timestamp); + + propagateWithChecks(group, timestamp, previousResult, previousResult, oldResult, newResult); + + AggregateResult previousOldResult = oldResult; + AggregateResult previousNewResult = newResult; + final TreeMap> groupEntries = this.memory + .get(group); + + Timestamp currentTimestamp = groupEntries == null ? null : groupEntries.higherKey(timestamp); + + while (currentTimestamp != null) { + final CumulativeAggregate groupEntry = groupEntries.get(currentTimestamp); + oldResult = groupEntry.result; + oldAccumulator = groupEntry.accumulator; + newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion); + newResult = operator.getAggregate(newAccumulator); + + storeIfNotNeutral(group, newAccumulator, newResult, currentTimestamp); + + propagateWithChecks(group, currentTimestamp, previousOldResult, previousNewResult, oldResult, newResult); + + previousOldResult = oldResult; + previousNewResult = newResult; + currentTimestamp = groupEntries.higherKey(currentTimestamp); + } + } + + @Override + protected Accumulator getAccumulator(final Tuple group, final Timestamp timestamp) { + final TreeMap> entryMap = this.memory.get(group); + if (entryMap == null) { + return operator.createNeutral(); + } else { + final CumulativeAggregate entry = entryMap.get(timestamp); + if (entry == null) { + final Entry> lowerEntry = entryMap + .lowerEntry(timestamp); + if (lowerEntry == null) { + return operator.createNeutral(); + } else { + return operator.clone(lowerEntry.getValue().accumulator); + } + } else { + return entry.accumulator; + } + } + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..79197aac --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlySequentialTimelyColumnAggregatorNode.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation.timely; + +import java.util.Objects; +import java.util.TreeMap; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * First-only column aggregator with sequential aggregation architecture. + * + * @author Tamas Szabo + * @since 2.4 + */ +public class FirstOnlySequentialTimelyColumnAggregatorNode + extends FirstOnlyTimelyColumnAggregatorNode { + + public FirstOnlySequentialTimelyColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final TupleMask groupMask, final TupleMask columnMask) { + super(reteContainer, operator, groupMask, columnMask); + } + + /** + * Accumulator gets modified only at the timestamp where the update happened. During the folding, accumulators are + * never changed at higher timestamps. Aggregate results at higher timestamps may change due to the change at the + * input timestamp. Uniqueness enforcement may require from aggregate results to jump up/down on demand during the + * folding. + */ + @Override + public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { + final Tuple group = groupMask.transform(update); + final Tuple value = columnMask.transform(update); + @SuppressWarnings("unchecked") + final Domain aggregand = (Domain) runtimeContext.unwrapElement(value.get(0)); + final boolean isInsertion = direction == Direction.INSERT; + + final AggregateResult previousResult = getResultRaw(group, timestamp, true); + + final Accumulator oldAccumulator = getAccumulator(group, timestamp); + final AggregateResult oldResult = previousResult == null ? operator.getAggregate(oldAccumulator) + : operator.combine(previousResult, oldAccumulator); + + final Accumulator newAccumulator = operator.update(oldAccumulator, aggregand, isInsertion); + final AggregateResult newResult = previousResult == null ? operator.getAggregate(newAccumulator) + : operator.combine(previousResult, newAccumulator); + + storeIfNotNeutral(group, newAccumulator, newResult, timestamp); + + propagateWithChecks(group, timestamp, previousResult, previousResult, oldResult, newResult); + + // fold up the state towards higher timestamps + if (!Objects.equals(oldResult, newResult)) { + AggregateResult previousOldResult = oldResult; + AggregateResult previousNewResult = newResult; + AggregateResult currentOldResult = null; + AggregateResult currentNewResult = null; + final TreeMap> groupEntries = this.memory + .get(group); + + Timestamp currentTimestamp = groupEntries == null ? null : groupEntries.higherKey(timestamp); + + while (currentTimestamp != null) { + // they cannot be the same, otherwise we would not even be here + assert !Objects.equals(previousOldResult, previousNewResult); + + final Accumulator accumulator = getAccumulator(group, currentTimestamp); + currentOldResult = groupEntries.get(currentTimestamp).result; + currentNewResult = operator.combine(previousNewResult, accumulator); + + // otherwise we would not be iterating over this timestamp + assert !operator.isNeutral(accumulator); + + propagateWithChecks(group, currentTimestamp, previousOldResult, previousNewResult, currentOldResult, + currentNewResult); + + if (!Objects.equals(currentOldResult, currentNewResult)) { + storeIfNotNeutral(group, accumulator, currentNewResult, currentTimestamp); + previousOldResult = currentOldResult; + previousNewResult = currentNewResult; + currentTimestamp = groupEntries.higherKey(currentTimestamp); + } else { + // we can stop the folding from here + break; + } + } + } + } + + @Override + protected Accumulator getAccumulator(final Tuple group, final Timestamp timestamp) { + final TreeMap> entryMap = this.memory.get(group); + if (entryMap == null) { + return operator.createNeutral(); + } else { + final CumulativeAggregate entry = entryMap.get(timestamp); + if (entry == null) { + return operator.createNeutral(); + } else { + return entry.accumulator; + } + } + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java new file mode 100644 index 00000000..0c73000e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FirstOnlyTimelyColumnAggregatorNode.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.aggregation.timely; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.TreeMap; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; +import tools.refinery.viatra.runtime.rete.aggregation.AbstractColumnAggregatorNode; +import tools.refinery.viatra.runtime.rete.aggregation.GroupedMap; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; + +/** + * First-only timely implementation of the column aggregator node. Only timestamps of appearance are maintained for + * tuples instead of complete timelines. + *

+ * Subclasses are responsible for implementing the aggregator architecture, and they must make use of the inner class {@link CumulativeAggregate}. + *

+ * This node supports recursive aggregation. + * + * @author Tamas Szabo + * @since 2.4 + */ +public abstract class FirstOnlyTimelyColumnAggregatorNode + extends AbstractColumnAggregatorNode { + + protected final Map>> memory; + + public FirstOnlyTimelyColumnAggregatorNode(final ReteContainer reteContainer, + final IMultisetAggregationOperator operator, + final TupleMask groupMask, final TupleMask columnMask) { + super(reteContainer, operator, groupMask, columnMask); + this.memory = CollectionsFactory.createMap(); + // mailbox MUST be instantiated after the fields are all set + this.mailbox = instantiateMailbox(); + } + + protected static class CumulativeAggregate { + // the accumulator storing the aggregands + protected Accumulator accumulator; + // the aggregate result at the timestamp where this cumulative aggregate is stored + protected AggregateResult result; + + private CumulativeAggregate(final Accumulator accumulator, final AggregateResult result) { + this.accumulator = accumulator; + this.result = result; + } + + } + + public Collection getGroups() { + return this.memory.keySet(); + } + + public AggregateResult getLastResult(final Tuple group) { + final TreeMap> groupMap = this.memory.get(group); + if (groupMap == null) { + return null; + } else { + return groupMap.lastEntry().getValue().result; + } + } + + public Timestamp getLastTimestamp(final Tuple group) { + final TreeMap> groupMap = this.memory.get(group); + if (groupMap == null) { + return null; + } else { + return groupMap.lastEntry().getKey(); + } + } + + @Override + protected Mailbox instantiateMailbox() { + return new TimelyMailbox(this, this.reteContainer); + } + + @Override + public void clear() { + this.mailbox.clear(); + this.memory.clear(); + this.children.clear(); + this.childMailboxes.clear(); + } + + protected void propagateWithChecks(final Tuple group, final Timestamp timestamp, + final AggregateResult previousOldResult, final AggregateResult previousNewResult, + final AggregateResult currentOldResult, final AggregateResult currentNewResult) { + final boolean jumpDown = Objects.equals(previousNewResult, currentOldResult); + final boolean jumpUp = Objects.equals(previousOldResult, currentNewResult); + final boolean resultsDiffer = !Objects.equals(currentOldResult, currentNewResult); + + // uniqueness enforcement is happening here + if ((resultsDiffer || jumpDown) && !Objects.equals(previousOldResult, currentOldResult)) { + propagate(Direction.DELETE, group, currentOldResult, timestamp); + } + if ((resultsDiffer || jumpUp) && !Objects.equals(previousNewResult, currentNewResult)) { + propagate(Direction.INSERT, group, currentNewResult, timestamp); + } + } + + /** + * Returns the aggregation architecture-specific accumulator at the specified timestamp for the given group. + */ + protected abstract Accumulator getAccumulator(final Tuple group, final Timestamp timestamp); + + protected AggregateResult getResultRaw(final Tuple group, final Timestamp timestamp, final boolean lower) { + final TreeMap> entryMap = this.memory.get(group); + if (entryMap == null) { + return null; + } else { + CumulativeAggregate entry = null; + if (lower) { + final Entry> lowerEntry = entryMap + .lowerEntry(timestamp); + if (lowerEntry != null) { + entry = lowerEntry.getValue(); + } + } else { + entry = entryMap.get(timestamp); + } + if (entry == null) { + return null; + } else { + return entry.result; + } + } + } + + protected AggregateResult getResult(final Tuple group, final Timestamp timestamp, final boolean lower) { + final AggregateResult result = getResultRaw(group, timestamp, lower); + if (result == null) { + return NEUTRAL; + } else { + return result; + } + } + + protected AggregateResult getResult(final Tuple group, final Timestamp timestamp) { + return getResult(group, timestamp, false); + } + + protected void storeIfNotNeutral(final Tuple group, final Accumulator accumulator, final AggregateResult value, + final Timestamp timestamp) { + TreeMap> entryMap = this.memory.get(group); + if (operator.isNeutral(accumulator)) { + if (entryMap != null) { + entryMap.remove(timestamp); + if (entryMap.isEmpty()) { + this.memory.remove(group); + } + } + } else { + if (entryMap == null) { + entryMap = CollectionsFactory.createTreeMap(); + this.memory.put(group, entryMap); + } + entryMap.put(timestamp, new CumulativeAggregate<>(accumulator, value)); + } + } + + @Override + public Tuple getAggregateTuple(final Tuple group) { + return tupleFromAggregateResult(group, getResult(group, Timestamp.ZERO)); + } + + @Override + public AggregateResult getAggregateResult(final Tuple group) { + return getResult(group, Timestamp.ZERO); + } + + @Override + public Map> getAggregateResultTimeline(final Tuple group) { + final TreeMap> entryMap = this.memory.get(group); + if (entryMap == null) { + return Collections.emptyMap(); + } else { + final Map> result = CollectionsFactory.createMap(); + for (final Entry> entry : entryMap + .descendingMap().entrySet()) { + result.put(entry.getValue().result, Timelines.createFrom(entry.getKey())); + } + return result; + } + } + + @Override + public Map> getAggregateTupleTimeline(final Tuple group) { + final Map> resultTimelines = getAggregateResultTimeline(group); + return new GroupedMap>(group, resultTimelines, this.runtimeContext); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java new file mode 100644 index 00000000..7bbf74ea --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/Disconnectable.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.boundary; + +/** + * For objects that connect a RETE implementation to the underlying model. + * + * @author Gabor Bergmann + * + */ +public interface Disconnectable { + + /** + * Disconnects this rete engine component from the underlying model. Disconnecting enables the garbage collection + * mechanisms to dispose of the rete network. + */ + void disconnect(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java new file mode 100644 index 00000000..51f89b52 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputEnumeratorNode.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.boundary; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContextListener; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.matcher.ReteEngine; +import tools.refinery.viatra.runtime.rete.network.Network; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.StandardNode; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; +import tools.refinery.viatra.runtime.rete.remote.Address; + +/** + * An input node representing an enumerable extensional input relation and receiving external updates. + * + *

+ * Contains those tuples that are in the extensional relation identified by the input key, and also conform to the + * global seed (if any). + * + * @author Bergmann Gabor + * + */ +public class ExternalInputEnumeratorNode extends StandardNode + implements Disconnectable, Receiver, IQueryRuntimeContextListener { + + private IQueryRuntimeContext context = null; + private IInputKey inputKey; + private Tuple globalSeed; + private InputConnector inputConnector; + private Network network; + private Address myAddress; + private boolean parallelExecutionEnabled; + /** + * @since 1.6 + */ + protected final Mailbox mailbox; + private final IQueryBackendContext qBackendContext; + + public ExternalInputEnumeratorNode(ReteContainer reteContainer) { + super(reteContainer); + myAddress = Address.of(this); + network = reteContainer.getNetwork(); + inputConnector = network.getInputConnector(); + qBackendContext = network.getEngine().getBackendContext(); + mailbox = instantiateMailbox(); + reteContainer.registerClearable(mailbox); + } + + /** + * Instantiates the {@link Mailbox} of this receiver. Subclasses may override this method to provide their own + * mailbox implementation. + * + * @return the mailbox + * @since 2.0 + */ + protected Mailbox instantiateMailbox() { + if (this.reteContainer.isTimelyEvaluation()) { + return new TimelyMailbox(this, this.reteContainer); + } else { + return new BehaviorChangingMailbox(this, this.reteContainer); + } + } + + @Override + public Mailbox getMailbox() { + return this.mailbox; + } + + public void connectThroughContext(ReteEngine engine, IInputKey inputKey, Tuple globalSeed) { + this.inputKey = inputKey; + this.globalSeed = globalSeed; + setTag(inputKey); + + final IQueryRuntimeContext context = engine.getRuntimeContext(); + if (!context.getMetaContext().isEnumerable(inputKey)) + throw new IllegalArgumentException(this.getClass().getSimpleName() + + " only applicable for enumerable input keys; received instead " + inputKey); + + this.context = context; + this.parallelExecutionEnabled = engine.isParallelExecutionEnabled(); + + engine.addDisconnectable(this); + context.addUpdateListener(inputKey, globalSeed, this); + } + + @Override + public void disconnect() { + if (context != null) { // if connected + context.removeUpdateListener(inputKey, globalSeed, this); + context = null; + } + } + + /** + * @since 2.2 + */ + protected Iterable getTuplesInternal() { + Iterable tuples = null; + + if (context != null) { // if connected + if (globalSeed == null) { + tuples = context.enumerateTuples(inputKey, TupleMask.empty(inputKey.getArity()), + Tuples.staticArityFlatTupleOf()); + } else { + final TupleMask mask = TupleMask.fromNonNullIndices(globalSeed); + tuples = context.enumerateTuples(inputKey, mask, mask.transform(globalSeed)); + } + } + + return tuples; + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + final Iterable tuples = getTuplesInternal(); + if (tuples != null) { + for (final Tuple tuple : tuples) { + collector.add(tuple); + } + } + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + final Iterable tuples = getTuplesInternal(); + if (tuples != null) { + for (final Tuple tuple : tuples) { + collector.put(tuple, Timestamp.INSERT_AT_ZERO_TIMELINE); + } + } + } + + /* Update from runtime context */ + @Override + public void update(IInputKey key, Tuple update, boolean isInsertion) { + if (parallelExecutionEnabled) { + // send back to myself as an official external update, and then propagate it transparently + network.sendExternalUpdate(myAddress, direction(isInsertion), update); + } else { + if (qBackendContext.areUpdatesDelayed()) { + // post the update into the mailbox of the node + mailbox.postMessage(direction(isInsertion), update, Timestamp.ZERO); + } else { + // just propagate the input + update(direction(isInsertion), update, Timestamp.ZERO); + } + // if the the update method is called from within a delayed execution, + // the following invocation will be a no-op + network.waitForReteTermination(); + } + } + + private static Direction direction(boolean isInsertion) { + return isInsertion ? Direction.INSERT : Direction.DELETE; + } + + /* Self-addressed from network */ + @Override + public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { + propagateUpdate(direction, updateElement, timestamp); + } + + @Override + public void appendParent(Supplier supplier) { + throw new UnsupportedOperationException("Input nodes can't have parents"); + } + + @Override + public void removeParent(Supplier supplier) { + throw new UnsupportedOperationException("Input nodes can't have parents"); + } + + @Override + public Collection getParents() { + return Collections.emptySet(); + } + + public IInputKey getInputKey() { + return inputKey; + } + + public Tuple getGlobalSeed() { + return globalSeed; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java new file mode 100644 index 00000000..57e06911 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ExternalInputStatelessFilterNode.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.boundary; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.rete.matcher.ReteEngine; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.single.FilterNode; + +/** + * A filter node representing a (stateless, typically non-enumerable) extensional input relation. + * + *

Contains those tuples of its parents, that (when transformed by a mask, if given) are present in the extensional relation identified by the input key. + * + * @author Bergmann Gabor + * + */ +public class ExternalInputStatelessFilterNode extends FilterNode implements Disconnectable { + + IQueryRuntimeContext context = null; + IInputKey inputKey; + private InputConnector inputConnector; + private TupleMask mask; + + public ExternalInputStatelessFilterNode(ReteContainer reteContainer, TupleMask mask) { + super(reteContainer); + this.mask = mask; + this.inputConnector = reteContainer.getNetwork().getInputConnector(); + } + + @Override + public boolean check(Tuple ps) { + if (mask != null) + ps = mask.transform(ps); + return context.containsTuple(inputKey, ps); + } + + + public void connectThroughContext(ReteEngine engine, IInputKey inputKey) { + this.inputKey = inputKey; + setTag(inputKey); + + final IQueryRuntimeContext context = engine.getRuntimeContext(); + if (!context.getMetaContext().isStateless(inputKey)) + throw new IllegalArgumentException( + this.getClass().getSimpleName() + + " only applicable for stateless input keys; received instead " + + inputKey); + + this.context = context; + + engine.addDisconnectable(this); + } + + @Override + public void disconnect() { + this.context = null; + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java new file mode 100644 index 00000000..c044850f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/InputConnector.java @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.boundary; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.rete.network.Network; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.recipes.InputFilterRecipe; +import tools.refinery.viatra.runtime.rete.recipes.InputRecipe; +import tools.refinery.viatra.runtime.rete.remote.Address; + +/** + * A class responsible for connecting input nodes to the runtime context. + * + * @author Bergmann Gabor + * + */ +public final class InputConnector { + Network network; + + private Map>> externalInputRoots = CollectionsFactory.createMap(); + +// /* +// * arity:1 used as simple entity constraints label is the object representing the type null label means all entities +// * regardless of type (global supertype), if allowed +// */ +// protected Map> unaryRoots = CollectionsFactory.getMap(); +// /* +// * arity:3 (rel, from, to) used as VPM relation constraints null label means all relations regardless of type +// * (global supertype) +// */ +// protected Map> ternaryEdgeRoots = CollectionsFactory.getMap(); +// /* +// * arity:2 (from, to) not used over VPM; can be used as EMF references for instance label is the object representing +// * the type null label means all entities regardless of type if allowed (global supertype), if allowed +// */ +// protected Map> binaryEdgeRoots = CollectionsFactory.getMap(); +// +// protected Address containmentRoot = null; +// protected Address containmentTransitiveRoot = null; +// protected Address instantiationRoot = null; +// protected Address instantiationTransitiveRoot = null; +// protected Address generalizationRoot = null; +// protected Address generalizationTransitiveRoot = null; + + + public InputConnector(Network network) { + super(); + this.network = network; + } + + + public Network getNetwork() { + return network; + } + + + /** + * Connects a given input filter node to the external input source. + */ + public void connectInputFilter(InputFilterRecipe recipe, Node freshNode) { + final ExternalInputStatelessFilterNode inputNode = (ExternalInputStatelessFilterNode)freshNode; + + IInputKey inputKey = (IInputKey) recipe.getInputKey(); + inputNode.connectThroughContext(network.getEngine(), inputKey); + } + + + /** + * Connects a given input enumerator node to the external input source. + */ + public void connectInput(InputRecipe recipe, Node freshNode) { + final ExternalInputEnumeratorNode inputNode = (ExternalInputEnumeratorNode)freshNode; + + IInputKey inputKey = (IInputKey) recipe.getInputKey(); + Tuple seed = nopSeed(inputKey); // no preseeding as of now + final Address freshAddress = Address.of(inputNode); + externalInputRoots.computeIfAbsent(inputKey, k -> CollectionsFactory.createMap()).put(seed, freshAddress); + inputNode.connectThroughContext(network.getEngine(), inputKey, seed); + +// final Address freshAddress = Address.of((Tunnel)freshNode); +// if (recipe instanceof TypeInputRecipe) { +// final Object typeKey = ((TypeInputRecipe) recipe).getTypeKey(); +// +// if (recipe instanceof UnaryInputRecipe) { +// unaryRoots.put(typeKey, freshAddress); +// new EntityFeeder(freshAddress, this, typeKey).feed(); +//// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) { +//// Collection subTypes = context.enumerateDirectUnarySubtypes(typeObject); +//// +//// for (Object subType : subTypes) { +//// Address subRoot = accessUnaryRoot(subType); +//// network.connectRemoteNodes(subRoot, tn, true); +//// } +//// } +// } else if (recipe instanceof BinaryInputRecipe) { +// binaryEdgeRoots.put(typeKey, freshAddress); +// externalInputRoots.put(rowKey, columnKey, freshAddress); +// new ReferenceFeeder(freshAddress, this, typeKey).feed(); +// // if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) { +// // Collection subTypes = context.enumerateDirectTernaryEdgeSubtypes(typeObject); +// // +// // for (Object subType : subTypes) { +// // Address subRoot = accessTernaryEdgeRoot(subType); +// // network.connectRemoteNodes(subRoot, tn, true); +// // } +// // } +// } +// +// +// } + + } + +// /** +// * fetches the entity Root node under specified label; returns null if it doesn't exist yet +// */ +// public Address getUnaryRoot(Object label) { +// return unaryRoots.get(label); +// } +// +// public Collection> getAllUnaryRoots() { +// return unaryRoots.values(); +// } +// +// /** +// * fetches the relation Root node under specified label; returns null if it doesn't exist yet +// */ +// public Address getTernaryEdgeRoot(Object label) { +// return ternaryEdgeRoots.get(label); +// } +// +// public Collection> getAllTernaryEdgeRoots() { +// return ternaryEdgeRoots.values(); +// } +// +// /** +// * fetches the reference Root node under specified label; returns null if it doesn't exist yet +// */ +// public Address getBinaryEdgeRoot(Object label) { +// return binaryEdgeRoots.get(label); +// } +// +// public Collection> getAllBinaryEdgeRoots() { +// return binaryEdgeRoots.values(); +// } +// +// +// public Address getContainmentRoot() { +// return containmentRoot; +// } +// +// +// public Address getContainmentTransitiveRoot() { +// return containmentTransitiveRoot; +// } +// +// +// public Address getInstantiationRoot() { +// return instantiationRoot; +// } +// +// +// public Address getInstantiationTransitiveRoot() { +// return instantiationTransitiveRoot; +// } +// +// +// public Address getGeneralizationRoot() { +// return generalizationRoot; +// } + + + public Stream> getAllExternalInputNodes() { + return externalInputRoots.values().stream().flatMap(map -> map.values().stream()); + } + public Collection> getAllExternalInputNodesForKey(IInputKey inputKey) { + return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).values(); + } + public Address getExternalInputNodeForKeyUnseeded(IInputKey inputKey) { + return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).get(nopSeed(inputKey)); + } + public Address getExternalInputNode(IInputKey inputKey, Tuple seed) { + if (seed == null) seed = nopSeed(inputKey); + return externalInputRoots.getOrDefault(inputKey, Collections.emptyMap()).get(seed); + } + + + Tuple nopSeed(IInputKey inputKey) { + return Tuples.flatTupleOf(new Object[inputKey.getArity()]); + } + + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java new file mode 100644 index 00000000..fe9c795e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/boundary/ReteBoundary.java @@ -0,0 +1,551 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.boundary; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.matcher.ReteEngine; +import tools.refinery.viatra.runtime.rete.network.Network; +import tools.refinery.viatra.runtime.rete.network.ProductionNode; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.remote.Address; +import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; +import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; + +/** + * Responsible for the storage, maintenance and communication of the nodes of the network that are accessible form the + * outside for various reasons. + * + * @author Gabor Bergmann + * + *

TODO: should eventually be merged into {@link InputConnector} and deleted + * + */ +public class ReteBoundary /*implements IPatternMatcherRuntimeContextListener*/ { + + protected ReteEngine engine; + protected Network network; + protected ReteContainer headContainer; + + public ReteContainer getHeadContainer() { + return headContainer; + } + + protected final InputConnector inputConnector; + + + protected Map> subplanToAddressMapping; + + + /** + * SubPlans of parent nodes that have the key node as their child. For RETE --> SubPlan traceability, mainly at production + * nodes. + */ + protected Map, Set> parentPlansOfReceiver; + + /** + * Prerequisite: engine has its network and framework fields initialized + */ + public ReteBoundary(ReteEngine engine) { + super(); + this.engine = engine; + this.network = engine.getReteNet(); + this.headContainer = network.getHeadContainer(); + inputConnector = network.getInputConnector(); + + this.parentPlansOfReceiver = CollectionsFactory.createMap(); + + // productionsScoped = new HashMap,Address>>(); + subplanToAddressMapping = CollectionsFactory.createMap(); + + } + + public Collection getAllProductionNodes() { + return engine.getCompiler().getCachedCompiledQueries().values(); + } + +// /** +// * accesses the entity Root node under specified label; creates the node if it doesn't exist yet +// */ +// public Address accessUnaryRoot(Object typeObject) { +// Address tn; +// tn = unaryRoots.get(typeObject); +// if (tn == null) { +// tn = headContainer.getProvisioner().newUniquenessEnforcerNode(1, typeObject); +// unaryRoots.put(typeObject, tn); +// +// new EntityFeeder(tn, context, network, this, typeObject).feed(); +// +// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) { +// Collection subTypes = context.enumerateDirectUnarySubtypes(typeObject); +// +// for (Object subType : subTypes) { +// Address subRoot = accessUnaryRoot(subType); +// network.connectRemoteNodes(subRoot, tn, true); +// } +// } +// +// } +// return tn; +// } +// +// /** +// * accesses the relation Root node under specified label; creates the node if it doesn't exist yet +// */ +// public Address accessTernaryEdgeRoot(Object typeObject) { +// Address tn; +// tn = ternaryEdgeRoots.get(typeObject); +// if (tn == null) { +// tn = headContainer.getProvisioner().newUniquenessEnforcerNode(3, typeObject); +// ternaryEdgeRoots.put(typeObject, tn); +// +// new RelationFeeder(tn, context, network, this, typeObject).feed(); +// +// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) { +// Collection subTypes = context.enumerateDirectTernaryEdgeSubtypes(typeObject); +// +// for (Object subType : subTypes) { +// Address subRoot = accessTernaryEdgeRoot(subType); +// network.connectRemoteNodes(subRoot, tn, true); +// } +// } +// } +// return tn; +// } +// +// /** +// * accesses the reference Root node under specified label; creates the node if it doesn't exist yet +// */ +// public Address accessBinaryEdgeRoot(Object typeObject) { +// Address tn; +// tn = binaryEdgeRoots.get(typeObject); +// if (tn == null) { +// tn = headContainer.getProvisioner().newUniquenessEnforcerNode(2, typeObject); +// binaryEdgeRoots.put(typeObject, tn); +// +// new ReferenceFeeder(tn, context, network, this, typeObject).feed(); +// +// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.BOTH) { +// Collection subTypes = context.enumerateDirectBinaryEdgeSubtypes(typeObject); +// +// for (Object subType : subTypes) { +// Address subRoot = accessBinaryEdgeRoot(subType); +// network.connectRemoteNodes(subRoot, tn, true); +// } +// } +// } +// return tn; +// } +// +// /** +// * accesses the special direct containment relation Root node; creates the node if it doesn't exist yet +// */ +// public Address accessContainmentRoot() { +// if (containmentRoot == null) { +// // containment: relation quasi-type +// containmentRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$containment"); +// +// new ContainmentFeeder(containmentRoot, context, network, this).feed(); +// } +// return containmentRoot; +// } +// +// /** +// * accesses the special transitive containment relation Root node; creates the node if it doesn't exist yet +// */ +// public Address accessContainmentTransitiveRoot() { +// if (containmentTransitiveRoot == null) { +// // transitive containment: derived +// Address containmentTransitiveRoot = headContainer.getProvisioner().newUniquenessEnforcerNode( +// 2, "$containmentTransitive"); +// network.connectRemoteNodes(accessContainmentRoot(), containmentTransitiveRoot, true); +// +// final int[] actLI = { 1 }; +// final int arcLIw = 2; +// final int[] actRI = { 0 }; +// final int arcRIw = 2; +// Address jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer( +// accessContainmentRoot(), new TupleMask(actLI, arcLIw)); +// Address jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer( +// containmentTransitiveRoot, new TupleMask(actRI, arcRIw)); +// +// final int[] actRIcomp = { 1 }; +// final int arcRIwcomp = 2; +// TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp); +// +// Address andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot, +// complementerMask); +// +// final int[] mask = { 0, 2 }; +// final int maskw = 3; +// Address tr = headContainer.getProvisioner().accessTrimmerNode(andCT, new TupleMask(mask, maskw)); +// network.connectRemoteNodes(tr, containmentTransitiveRoot, true); +// +// this.containmentTransitiveRoot = containmentTransitiveRoot; // cast +// // back +// // to +// // Supplier +// } +// return containmentTransitiveRoot; +// } +// +// /** +// * accesses the special instantiation relation Root node; creates the node if it doesn't exist yet +// */ +// public Address accessInstantiationRoot() { +// if (instantiationRoot == null) { +// // instantiation: relation quasi-type +// instantiationRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$instantiation"); +// +// new InstantiationFeeder(instantiationRoot, context, network, this).feed(); +// } +// return instantiationRoot; +// } +// +// /** +// * accesses the special transitive instantiation relation Root node; creates the node if it doesn't exist yet +// * InstantiationTransitive = Instantiation o (Generalization)^* +// */ +// public Address accessInstantiationTransitiveRoot() { +// if (instantiationTransitiveRoot == null) { +// // transitive instantiation: derived +// Address instantiationTransitiveRoot = headContainer.getProvisioner() +// .newUniquenessEnforcerNode(2, "$instantiationTransitive"); +// network.connectRemoteNodes(accessInstantiationRoot(), instantiationTransitiveRoot, true); +// +// final int[] actLI = { 1 }; +// final int arcLIw = 2; +// final int[] actRI = { 0 }; +// final int arcRIw = 2; +// Address jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer( +// accessGeneralizationRoot(), new TupleMask(actLI, arcLIw)); +// Address jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer( +// instantiationTransitiveRoot, new TupleMask(actRI, arcRIw)); +// +// final int[] actRIcomp = { 1 }; +// final int arcRIwcomp = 2; +// TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp); +// +// Address andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot, +// complementerMask); +// +// final int[] mask = { 0, 2 }; +// final int maskw = 3; +// Address tr = headContainer.getProvisioner().accessTrimmerNode(andCT, +// new TupleMask(mask, maskw)); +// network.connectRemoteNodes(tr, instantiationTransitiveRoot, true); +// +// this.instantiationTransitiveRoot = instantiationTransitiveRoot; // cast +// // back +// // to +// // Supplier +// } +// return instantiationTransitiveRoot; +// } +// +// /** +// * accesses the special generalization relation Root node; creates the node if it doesn't exist yet +// */ +// public Address accessGeneralizationRoot() { +// if (generalizationRoot == null) { +// // generalization: relation quasi-type +// generalizationRoot = headContainer.getProvisioner().newUniquenessEnforcerNode(2, "$generalization"); +// +// new GeneralizationFeeder(generalizationRoot, context, network, this).feed(); +// } +// return generalizationRoot; +// } +// +// /** +// * accesses the special transitive containment relation Root node; creates the node if it doesn't exist yet +// */ +// public Address accessGeneralizationTransitiveRoot() { +// if (generalizationTransitiveRoot == null) { +// // transitive generalization: derived +// Address generalizationTransitiveRoot = headContainer.getProvisioner() +// .newUniquenessEnforcerNode(2, "$generalizationTransitive"); +// network.connectRemoteNodes(accessGeneralizationRoot(), generalizationTransitiveRoot, true); +// +// final int[] actLI = { 1 }; +// final int arcLIw = 2; +// final int[] actRI = { 0 }; +// final int arcRIw = 2; +// Address jPrimarySlot = headContainer.getProvisioner().accessProjectionIndexer( +// accessGeneralizationRoot(), new TupleMask(actLI, arcLIw)); +// Address jSecondarySlot = headContainer.getProvisioner().accessProjectionIndexer( +// generalizationTransitiveRoot, new TupleMask(actRI, arcRIw)); +// +// final int[] actRIcomp = { 1 }; +// final int arcRIwcomp = 2; +// TupleMask complementerMask = new TupleMask(actRIcomp, arcRIwcomp); +// +// Address andCT = headContainer.getProvisioner().accessJoinNode(jPrimarySlot, jSecondarySlot, +// complementerMask); +// +// final int[] mask = { 0, 2 }; +// final int maskw = 3; +// Address tr = headContainer.getProvisioner().accessTrimmerNode(andCT, new TupleMask(mask, maskw)); +// network.connectRemoteNodes(tr, generalizationTransitiveRoot, true); +// +// this.generalizationTransitiveRoot = generalizationTransitiveRoot; // cast +// // back +// // to +// // Supplier +// } +// return generalizationTransitiveRoot; +// } + + // /** + // * Registers and publishes a supplier under specified label. + // */ + // public void publishSupplier(Supplier s, Object label) + // { + // publishedSuppliers.put(label, s); + // } + // + // /** + // * fetches the production node under specified label; + // * returns null if it doesn't exist yet + // */ + // public Production getProductionNode(Object label) + // { + // return productions.get(label); + // } + // + // /** + // * fetches the published supplier under specified label; + // * returns null if it doesn't exist yet + // */ + // public Supplier getPublishedSupplier(Object label) + // { + // return publishedSuppliers.get(label); + // } + + /** + * accesses the production node for specified pattern; builds pattern matcher if it doesn't exist yet + * @throws ViatraQueryRuntimeException + */ + public synchronized RecipeTraceInfo accessProductionTrace(PQuery query) + { + final CompiledQuery compiled = engine.getCompiler().getCompiledForm(query); + return compiled; +// RecipeTraceInfo pn; +// pn = queryPlans.get(query); +// if (pn == null) { +// pn = construct(query); +// TODO handle recursion by reinterpret-RecipeTrace +// queryPlans.put(query, pn); +// if (pn == null) { +// String[] args = { query.toString() }; +// throw new RetePatternBuildException("Unsuccessful planning of RETE construction recipe for query {1}", +// args, "Could not create RETE recipe plan.", query); +// } +// } +// return pn; + } + /** + * accesses the production node for specified pattern; builds pattern matcher if it doesn't exist yet + * @throws ViatraQueryRuntimeException + */ + public synchronized Address accessProductionNode(PQuery query) { + final RecipeTraceInfo productionTrace = accessProductionTrace(query); + return (Address) headContainer.getProvisioner().getOrCreateNodeByRecipe(productionTrace); + } + +// /** +// * creates the production node for the specified pattern Contract: only call from the builder (through Buildable) +// * responsible for building this pattern +// * +// * @throws PatternMatcherCompileTimeException +// * if production node is already created +// */ +// public synchronized Address createProductionInternal(PQuery gtPattern) +// throws QueryPlannerException { +// if (queryPlans.containsKey(gtPattern)) { +// String[] args = { gtPattern.toString() }; +// throw new RetePatternBuildException("Multiple creation attempts of production node for {1}", args, +// "Duplicate RETE production node.", gtPattern); +// } +// +// Map posMapping = engine.getBuilder().getPosMapping(gtPattern); +// Address pn = headContainer.getProvisioner().newProductionNode(posMapping, gtPattern); +// queryPlans.put(gtPattern, pn); +// context.reportPatternDependency(gtPattern); +// +// return pn; +// } + + // /** + // * accesses the production node for specified pattern and scope map; creates the node if + // * it doesn't exist yet + // */ + // public synchronized Address accessProductionScoped( + // GTPattern gtPattern, Map additionalScopeMap) throws PatternMatcherCompileTimeException { + // if (additionalScopeMap.isEmpty()) return accessProduction(gtPattern); + // + // Address pn; + // + // Map, Address> scopes = productionsScoped.get(gtPattern); + // if (scopes == null) { + // scopes = new HashMap, Address>(); + // productionsScoped.put(gtPattern, scopes); + // } + // + // pn = scopes.get(additionalScopeMap); + // if (pn == null) { + // Address unscopedProduction = accessProduction(gtPattern); + // + // HashMap posMapping = headContainer.resolveLocal(unscopedProduction).getPosMapping(); + // pn = headContainer.getLibrary().newProductionNode(posMapping); + // scopes.put(additionalScopeMap, pn); + // + // constructScoper(unscopedProduction, additionalScopeMap, pn); + // } + // return pn; + // } + + // protected void constructScoper( + // Address unscopedProduction, + // Map additionalScopeMap, + // Address production) + // throws PatternMatcherCompileTimeException { + // engine.reteNet.waitForReteTermination(); + // engine.builder.constructScoper(unscopedProduction, additionalScopeMap, production); + // } + + // /** + // * Invalidates the subnet constructed for the recognition of a given + // pattern. + // * The pattern matcher will have to be rebuilt. + // * @param gtPattern the pattern whose matcher subnet should be invalidated + // */ + // public void invalidatePattern(GTPattern gtPattern) { + // Production production = null; + // try { + // production = accessProduction(gtPattern); + // } catch (PatternMatcherCompileTimeException e) { + // // this should not occur here, since we already have a production node + // e.printStackTrace(); + // } + // + // production.tearOff(); + // //production.setDirty(true); + // } + + // updaters for change notification + // if the corresponding rete input isn't created yet, call is ignored + + private static Direction direction(boolean isInsertion) { + return isInsertion ? Direction.INSERT : Direction.DELETE; + } + +// @Override +// public void updateUnary(boolean isInsertion, Object entity, Object typeObject) { +// Address root = inputConnector.getUnaryRoot(typeObject); +// if (root != null) { +// network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(entity))); +// if (!engine.isParallelExecutionEnabled()) +// network.waitForReteTermination(); +// } +// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) { +// for (Object superType : context.enumerateDirectUnarySupertypes(typeObject)) { +// updateUnary(isInsertion, entity, superType); +// } +// } +// } +// +// @Override +// public void updateTernaryEdge(boolean isInsertion, Object relation, Object from, Object to, Object typeObject) { +// Address root = inputConnector.getTernaryEdgeRoot(typeObject); +// if (root != null) { +// network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(relation), inputConnector.wrapElement(from), +// inputConnector.wrapElement(to))); +// if (!engine.isParallelExecutionEnabled()) +// network.waitForReteTermination(); +// } +// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) { +// for (Object superType : context.enumerateDirectTernaryEdgeSupertypes(typeObject)) { +// updateTernaryEdge(isInsertion, relation, from, to, superType); +// } +// } +// } +// @Override +// public void updateBinaryEdge(boolean isInsertion, Object from, Object to, Object typeObject) { +// Address root = inputConnector.getBinaryEdgeRoot(typeObject); +// if (root != null) { +// network.sendExternalUpdate(root, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(from), inputConnector.wrapElement(to))); +// if (!engine.isParallelExecutionEnabled()) +// network.waitForReteTermination(); +// } +// if (typeObject != null && generalizationQueryDirection == GeneralizationQueryDirection.SUPERTYPE_ONLY) { +// for (Object superType : context.enumerateDirectBinaryEdgeSupertypes(typeObject)) { +// updateBinaryEdge(isInsertion, from, to, superType); +// } +// } +// } +// +// @Override +// public void updateContainment(boolean isInsertion, Object container, Object element) { +// final Address containmentRoot = inputConnector.getContainmentRoot(); +// if (containmentRoot != null) { +// network.sendExternalUpdate(containmentRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(container), +// inputConnector.wrapElement(element))); +// if (!engine.isParallelExecutionEnabled()) +// network.waitForReteTermination(); +// } +// } +// +// @Override +// public void updateInstantiation(boolean isInsertion, Object parent, Object child) { +// final Address instantiationRoot = inputConnector.getInstantiationRoot(); +// if (instantiationRoot != null) { +// network.sendExternalUpdate(instantiationRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(parent), +// inputConnector.wrapElement(child))); +// if (!engine.isParallelExecutionEnabled()) +// network.waitForReteTermination(); +// } +// } +// +// @Override +// public void updateGeneralization(boolean isInsertion, Object parent, Object child) { +// final Address generalizationRoot = inputConnector.getGeneralizationRoot(); +// if (generalizationRoot != null) { +// network.sendExternalUpdate(generalizationRoot, direction(isInsertion), new FlatTuple(inputConnector.wrapElement(parent), +// inputConnector.wrapElement(child))); +// if (!engine.isParallelExecutionEnabled()) +// network.waitForReteTermination(); +// } +// } + + // no wrapping needed! + public void notifyEvaluator(Address receiver, Tuple tuple) { + network.sendExternalUpdate(receiver, Direction.INSERT, tuple); + if (!engine.isParallelExecutionEnabled()) + network.waitForReteTermination(); + } + + public void mapPlanToAddress(SubPlan plan, Address handle) { + subplanToAddressMapping.put(plan, handle); + } + + public Address getAddress(SubPlan plan) { + return subplanToAddressMapping.get(plan); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java new file mode 100644 index 00000000..bd1b219e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/RetePatternBuildException.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.construction; + +import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; + +/** + * A problem has occurred during the construction of the RETE net. + * + * @author Gabor Bergmann + * + */ +public class RetePatternBuildException extends QueryProcessingException { + + private static final long serialVersionUID = 6966585498204577548L; + + /** + * @param message + * The template of the exception message + * @param context + * The data elements to be used to instantiate the template. Can be null if no context parameter is + * defined + * @param patternDescription + * the PatternDescription where the exception occurred + */ + public RetePatternBuildException(String message, String[] context, String shortMessage, Object patternDescription) { + super(message, context, shortMessage, patternDescription); + } + + /** + * @param message + * The template of the exception message + * @param context + * The data elements to be used to instantiate the template. Can be null if no context parameter is + * defined + * @param patternDescription + * the PatternDescription where the exception occurred + */ + public RetePatternBuildException(String message, String[] context, String shortMessage, Object patternDescription, + Throwable cause) { + super(message, context, shortMessage, patternDescription, cause); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java new file mode 100644 index 00000000..bd22e1a0 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/BasicLinearLayout.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.construction.basiclinear; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; + +import org.apache.log4j.Logger; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; +import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper; +import tools.refinery.viatra.runtime.matchers.planning.operations.PApply; +import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; +import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; +import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException; + +/** + * Basic layout that builds a linear RETE net based on a heuristic ordering of constraints. + * + * @author Gabor Bergmann + * + */ +public class BasicLinearLayout implements IQueryPlannerStrategy { + + //SubPlanProcessor planProcessor = new SubPlanProcessor(); + + private IQueryBackendHintProvider hintProvider; + private IQueryBackendContext bContext; + /** + * @param bContext + * @since 1.5 + */ + public BasicLinearLayout(IQueryBackendContext bContext) { + this.bContext = bContext; + this.hintProvider = bContext.getHintProvider(); + } + + @Override + public SubPlan plan(final PBody pSystem, Logger logger, IQueryMetaContext context) { + SubPlanFactory planFactory = new SubPlanFactory(pSystem); + PQuery query = pSystem.getPattern(); + //planProcessor.setCompiler(compiler); + try { + logger.debug(String.format( + "%s: patternbody build started for %s", + getClass().getSimpleName(), + query.getFullyQualifiedName())); + + // STARTING THE LINE + SubPlan plan = planFactory.createSubPlan(new PStart()); + + Set pQueue = CollectionsFactory.createSet(pSystem.getConstraints()); + + // MAIN LOOP + while (!pQueue.isEmpty()) { + PConstraint pConstraint = Collections.min(pQueue, + new OrderingHeuristics(plan, context)); // pQueue.iterator().next(); + pQueue.remove(pConstraint); + + // if we have no better option than an unready deferred constraint, raise error + if (pConstraint instanceof DeferredPConstraint) { + final DeferredPConstraint deferred = (DeferredPConstraint) pConstraint; + if (!deferred.isReadyAt(plan, context)) { + raiseForeverDeferredError(deferred, plan, context); + } + } + // TODO integrate the check above in SubPlan / POperation?? + + // replace incumbent plan with its child + plan = planFactory.createSubPlan(new PApply(pConstraint), plan); + } + + // PROJECT TO PARAMETERS + SubPlan finalPlan = planFactory.createSubPlan(new PProject(pSystem.getSymbolicParameterVariables()), plan); + + // FINAL CHECK, whether all exported variables are present + all constraint satisfied + BuildHelper.finalCheck(pSystem, finalPlan, context); + // TODO integrate the check above in SubPlan / POperation + + logger.debug(String.format( + "%s: patternbody query plan concluded for %s as: %s", + getClass().getSimpleName(), + query.getFullyQualifiedName(), + finalPlan.toLongString())); + + return finalPlan; + + } catch (RetePatternBuildException ex) { + ex.setPatternDescription(query); + throw ex; + } + } + + /** + * Called when the constraint is not ready, but cannot be deferred further. + * + * @param plan + * @throws RetePatternBuildException + * to indicate the error in detail. + */ + private void raiseForeverDeferredError(DeferredPConstraint constraint, SubPlan plan, IQueryMetaContext context) { + if (constraint instanceof Equality) { + raiseForeverDeferredError((Equality)constraint, plan, context); + } else if (constraint instanceof ExportedParameter) { + raiseForeverDeferredError((ExportedParameter)constraint, plan, context); + } else if (constraint instanceof ExpressionEvaluation) { + raiseForeverDeferredError((ExpressionEvaluation)constraint, plan, context); + } else if (constraint instanceof VariableDeferredPConstraint) { + raiseForeverDeferredError(constraint, plan, context); + } + } + + private void raiseForeverDeferredError(Equality constraint, SubPlan plan, IQueryMetaContext context) { + String[] args = { constraint.getWho().toString(), constraint.getWithWhom().toString() }; + String msg = "Cannot express equality of variables {1} and {2} if neither of them is deducable."; + String shortMsg = "Equality between undeducible variables."; + throw new RetePatternBuildException(msg, args, shortMsg, null); + } + private void raiseForeverDeferredError(ExportedParameter constraint, SubPlan plan, IQueryMetaContext context) { + String[] args = { constraint.getParameterName() }; + String msg = "Pattern Graph Search terminated incompletely: " + + "exported pattern variable {1} could not be determined based on the pattern constraints. " + + "HINT: certain constructs (e.g. negative patterns or check expressions) cannot output symbolic parameters."; + String shortMsg = "Could not deduce value of parameter"; + throw new RetePatternBuildException(msg, args, shortMsg, null); + } + private void raiseForeverDeferredError(ExpressionEvaluation constraint, SubPlan plan, IQueryMetaContext context) { + if (constraint.checkTypeSafety(plan, context) == null) { + raiseForeverDeferredError(constraint, plan); + } else { + String[] args = { toString(), constraint.checkTypeSafety(plan, context).toString() }; + String msg = "The checking of pattern constraint {1} cannot be deferred further, but variable {2} is still not type safe. " + + "HINT: the incremental matcher is not an equation solver, please make sure that all variable values are deducible."; + String shortMsg = "Could not check all constraints due to undeducible type restrictions"; + throw new RetePatternBuildException(msg, args, shortMsg, null); + } + } + private void raiseForeverDeferredError(VariableDeferredPConstraint constraint, SubPlan plan) { + Set missing = CollectionsFactory.createSet(constraint.getDeferringVariables());//new HashSet(getDeferringVariables()); + missing.removeAll(plan.getVisibleVariables()); + String[] args = { toString(), Arrays.toString(missing.toArray()) }; + String msg = "The checking of pattern constraint {1} requires the values of variables {2}, but it cannot be deferred further. " + + "HINT: the incremental matcher is not an equation solver, please make sure that all variable values are deducible."; + String shortMsg = "Could not check all constraints due to undeducible variables"; + throw new RetePatternBuildException(msg, args, shortMsg, null); + } + + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java new file mode 100644 index 00000000..2b4e8890 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/basiclinear/OrderingHeuristics.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.construction.basiclinear; + +import java.util.Comparator; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.rete.util.OrderingCompareAgent; + +/** + * @author Gabor Bergmann + * + */ +public class OrderingHeuristics implements Comparator { + private SubPlan plan; + private IQueryMetaContext context; + + public OrderingHeuristics(SubPlan plan, IQueryMetaContext context) { + super(); + this.plan = plan; + this.context = context; + } + + @Override + public int compare(PConstraint o1, PConstraint o2) { + return new OrderingCompareAgent(o1, o2) { + @Override + protected void doCompare() { + boolean temp = consider(preferTrue(isConstant(a), isConstant(b))) + && consider(preferTrue(isReady(a), isReady(b))); + if (!temp) + return; + + Set bound1 = boundVariables(a); + Set bound2 = boundVariables(b); + swallowBoolean(temp && consider(preferTrue(isBound(a, bound1), isBound(b, bound2))) + && consider(preferMore(degreeBound(a, bound1), degreeBound(b, bound2))) + && consider(preferLess(degreeFree(a, bound1), degreeFree(b, bound2))) + + // tie breaking + && consider(preferLess(a.getMonotonousID(), b.getMonotonousID())) // this is hopefully deterministic + && consider(preferLess(System.identityHashCode(a), System.identityHashCode(b)))); + } + }.compare(); + } + + boolean isConstant(PConstraint o) { + return (o instanceof ConstantValue); + } + + boolean isReady(PConstraint o) { + return (o instanceof EnumerablePConstraint) + || (o instanceof DeferredPConstraint && ((DeferredPConstraint) o) + .isReadyAt(plan, context)); + } + + Set boundVariables(PConstraint o) { + Set boundVariables = CollectionsFactory.createSet(o.getAffectedVariables()); + boundVariables.retainAll(plan.getVisibleVariables()); + return boundVariables; + } + + boolean isBound(PConstraint o, Set boundVariables) { + return boundVariables.size() == o.getAffectedVariables().size(); + } + + int degreeBound(PConstraint o, Set boundVariables) { + return boundVariables.size(); + } + + int degreeFree(PConstraint o, Set boundVariables) { + return o.getAffectedVariables().size() - boundVariables.size(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java new file mode 100644 index 00000000..da2fb432 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/CompilerHelper.java @@ -0,0 +1,390 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.construction.plancompiler; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; +import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; +import tools.refinery.viatra.runtime.rete.recipes.EqualityFilterRecipe; +import tools.refinery.viatra.runtime.rete.recipes.IndexerBasedAggregatorRecipe; +import tools.refinery.viatra.runtime.rete.recipes.IndexerRecipe; +import tools.refinery.viatra.runtime.rete.recipes.JoinRecipe; +import tools.refinery.viatra.runtime.rete.recipes.Mask; +import tools.refinery.viatra.runtime.rete.recipes.MonotonicityInfo; +import tools.refinery.viatra.runtime.rete.recipes.ProductionRecipe; +import tools.refinery.viatra.runtime.rete.recipes.ProjectionIndexerRecipe; +import tools.refinery.viatra.runtime.rete.recipes.RecipesFactory; +import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; +import tools.refinery.viatra.runtime.rete.recipes.SingleColumnAggregatorRecipe; +import tools.refinery.viatra.runtime.rete.recipes.TrimmerRecipe; +import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper; +import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; +import tools.refinery.viatra.runtime.rete.traceability.CompiledSubPlan; +import tools.refinery.viatra.runtime.rete.traceability.PlanningTrace; +import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; +import tools.refinery.viatra.runtime.rete.util.ReteHintOptions; + +/** + * @author Bergmann Gabor + * + */ +public class CompilerHelper { + + private CompilerHelper() {/*Utility class constructor*/} + + static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE; + + /** + * Makes sure that all variables in the tuple are different so that it can be used as {@link CompiledSubPlan}. If a + * variable occurs multiple times, equality checks are applied and then the results are trimmed so that duplicates + * are hidden. If no manipulation is necessary, the original trace is returned. + * + *

+ * to be used whenever a constraint introduces new variables. + */ + public static PlanningTrace checkAndTrimEqualVariables(SubPlan plan, final PlanningTrace coreTrace) { + // are variables in the constraint all different? + final List coreVariablesTuple = coreTrace.getVariablesTuple(); + final int constraintArity = coreVariablesTuple.size(); + final int distinctVariables = coreTrace.getPosMapping().size(); + if (constraintArity == distinctVariables) { + // all variables occur exactly once in tuple + return coreTrace; + } else { // apply equality checks and trim + + // find the positions in the tuple for each variable + Map> posMultimap = new HashMap>(); + List trimmedVariablesTuple = new ArrayList(distinctVariables); + int[] trimIndices = new int[distinctVariables]; + for (int i = 0; i < constraintArity; ++i) { + final PVariable variable = coreVariablesTuple.get(i); + SortedSet indexSet = posMultimap.get(variable); + if (indexSet == null) { // first occurrence of variable + indexSet = new TreeSet(); + posMultimap.put(variable, indexSet); + + // this is the first occurrence, set up trimming + trimIndices[trimmedVariablesTuple.size()] = i; + trimmedVariablesTuple.add(variable); + } + indexSet.add(i); + } + + // construct equality checks for each variable occurring multiple times + PlanningTrace lastTrace = coreTrace; + for (Entry> entry : posMultimap.entrySet()) { + if (entry.getValue().size() > 1) { + EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe(); + equalityFilterRecipe.setParent(lastTrace.getRecipe()); + equalityFilterRecipe.getIndices().addAll(entry.getValue()); + lastTrace = new PlanningTrace(plan, coreVariablesTuple, equalityFilterRecipe, lastTrace); + } + } + + // trim so that each variable occurs only once + TrimmerRecipe trimmerRecipe = FACTORY.createTrimmerRecipe(); + trimmerRecipe.setParent(lastTrace.getRecipe()); + trimmerRecipe.setMask(tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper + .mask(constraintArity, trimIndices)); + return new PlanningTrace(plan, trimmedVariablesTuple, trimmerRecipe, lastTrace); + } + } + + /** + * Extracts the variable list representation of the variables tuple. + */ + public static List convertVariablesTuple(EnumerablePConstraint constraint) { + return convertVariablesTuple(constraint.getVariablesTuple()); + } + + /** + * Extracts the variable list representation of the variables tuple. + */ + public static List convertVariablesTuple(Tuple variablesTuple) { + List result = new ArrayList(); + for (Object o : variablesTuple.getElements()) + result.add((PVariable) o); + return result; + } + + /** + * Returns a compiled indexer trace according to a mask + */ + public static RecipeTraceInfo makeIndexerTrace(SubPlan planToCompile, PlanningTrace parentTrace, TupleMask mask) { + final ReteNodeRecipe parentRecipe = parentTrace.getRecipe(); + if (parentRecipe instanceof IndexerBasedAggregatorRecipe + || parentRecipe instanceof SingleColumnAggregatorRecipe) + throw new IllegalArgumentException( + "Cannot take projection indexer of aggregator node at plan " + planToCompile); + IndexerRecipe recipe = RecipesHelper.projectionIndexerRecipe(parentRecipe, toRecipeMask(mask)); + // final List maskedVariables = mask.transform(parentTrace.getVariablesTuple()); + return new PlanningTrace(planToCompile, /* maskedVariables */ parentTrace.getVariablesTuple(), recipe, + parentTrace); + // TODO add specialized indexer trace info? + } + + /** + * Creates a trimmer that keeps selected variables only. + */ + protected static TrimmerRecipe makeTrimmerRecipe(final PlanningTrace compiledParent, + List projectedVariables) { + final Mask projectionMask = makeProjectionMask(compiledParent, projectedVariables); + final TrimmerRecipe trimmerRecipe = ReteRecipeCompiler.FACTORY.createTrimmerRecipe(); + trimmerRecipe.setParent(compiledParent.getRecipe()); + trimmerRecipe.setMask(projectionMask); + return trimmerRecipe; + } + + public static Mask makeProjectionMask(final PlanningTrace compiledParent, Iterable projectedVariables) { + List projectionSourceIndices = new ArrayList(); + for (PVariable pVariable : projectedVariables) { + projectionSourceIndices.add(compiledParent.getPosMapping().get(pVariable)); + } + final Mask projectionMask = RecipesHelper.mask(compiledParent.getRecipe().getArity(), projectionSourceIndices); + return projectionMask; + } + + /** + * @since 1.6 + */ + public static final class PosetTriplet { + public Mask coreMask; + public Mask posetMask; + public IPosetComparator comparator; + } + + /** + * @since 1.6 + */ + public static PosetTriplet computePosetInfo(List variables, PBody body, IQueryMetaContext context) { + Map> typeMap = TypeHelper.inferUnaryTypesFor(variables, body.getConstraints(), + context); + List> keys = new LinkedList>(); + + for (int i = 0; i < variables.size(); i++) { + keys.add(typeMap.get(variables.get(i))); + } + + return computePosetInfo(keys, context); + } + + /** + * @since 1.6 + */ + public static PosetTriplet computePosetInfo(List parameters, IQueryMetaContext context) { + List> keys = new LinkedList>(); + for (int i = 0; i < parameters.size(); i++) { + IInputKey key = parameters.get(i).getDeclaredUnaryType(); + if (key == null) { + keys.add(Collections.emptySet()); + } else { + keys.add(Collections.singleton(parameters.get(i).getDeclaredUnaryType())); + } + } + return computePosetInfo(keys, context); + } + + + + /** + * @since 1.6 + */ + public static PosetTriplet computePosetInfo(Iterable> keys, IQueryMetaContext context) { + PosetTriplet result = new PosetTriplet(); + List coreIndices = new ArrayList(); + List posetIndices = new ArrayList(); + List filtered = new ArrayList(); + boolean posetKey = false; + int index = -1; + + for (Set _keys : keys) { + ++index; + posetKey = false; + + for (IInputKey key : _keys) { + if (key != null && context.isPosetKey(key)) { + posetKey = true; + filtered.add(key); + break; + } + } + + if (posetKey) { + posetIndices.add(index); + } else { + coreIndices.add(index); + } + } + + result.comparator = context.getPosetComparator(filtered); + result.coreMask = RecipesHelper.mask(index + 1, coreIndices); + result.posetMask = RecipesHelper.mask(index + 1, posetIndices); + + return result; + } + + /** + * Creates a recipe for a production node and the corresponding trace. + *

PRE: in case this is a recursion cutoff point (see {@link RecursionCutoffPoint}) + * and bodyFinalTraces will be filled later, + * the object yielded now by bodyFinalTraces.values() must return up-to-date results later + * @since 2.4 + */ + public static CompiledQuery makeQueryTrace(PQuery query, Map bodyFinalTraces, + Collection bodyFinalRecipes, QueryEvaluationHint hint, IQueryMetaContext context, + boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) { + ProductionRecipe recipe = ReteRecipeCompiler.FACTORY.createProductionRecipe(); + + // temporary solution to support the deprecated option for now + boolean deleteAndRederiveEvaluationDep = deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(hint); + + recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); + + if (deleteAndRederiveEvaluationDep || (timelyEvaluation != null)) { + PosetTriplet triplet = computePosetInfo(query.getParameters(), context); + if (triplet.comparator != null) { + MonotonicityInfo info = FACTORY.createMonotonicityInfo(); + info.setCoreMask(triplet.coreMask); + info.setPosetMask(triplet.posetMask); + info.setPosetComparator(triplet.comparator); + recipe.setOptionalMonotonicityInfo(info); + } + } + + recipe.setPattern(query); + recipe.setPatternFQN(query.getFullyQualifiedName()); + recipe.setTraceInfo(recipe.getPatternFQN()); + recipe.getParents().addAll(bodyFinalRecipes); + for (int i = 0; i < query.getParameterNames().size(); ++i) { + recipe.getMappedIndices().put(query.getParameterNames().get(i), i); + } + + return new CompiledQuery(recipe, bodyFinalTraces, query); + } + + /** + * Calculated index mappings for a join, based on the common variables of the two parent subplans. + * + * @author Gabor Bergmann + * + */ + public static class JoinHelper { + private TupleMask primaryMask; + private TupleMask secondaryMask; + private TupleMask complementerMask; + private RecipeTraceInfo primaryIndexer; + private RecipeTraceInfo secondaryIndexer; + private JoinRecipe naturalJoinRecipe; + private List naturalJoinVariablesTuple; + + /** + * @pre enforceVariableCoincidences() has been called on both sides. + */ + public JoinHelper(SubPlan planToCompile, PlanningTrace primaryCompiled, PlanningTrace callTrace) { + super(); + + Set primaryVariables = new LinkedHashSet(primaryCompiled.getVariablesTuple()); + Set secondaryVariables = new LinkedHashSet(callTrace.getVariablesTuple()); + int oldNodes = 0; + Set introducingSecondaryIndices = new TreeSet(); + for (PVariable var : secondaryVariables) { + if (primaryVariables.contains(var)) + oldNodes++; + else + introducingSecondaryIndices.add(callTrace.getPosMapping().get(var)); + } + List primaryIndices = new ArrayList(oldNodes); + List secondaryIndices = new ArrayList(oldNodes); + for (PVariable var : secondaryVariables) { + if (primaryVariables.contains(var)) { + primaryIndices.add(primaryCompiled.getPosMapping().get(var)); + secondaryIndices.add(callTrace.getPosMapping().get(var)); + } + } + Collection complementerIndices = introducingSecondaryIndices; + + primaryMask = TupleMask.fromSelectedIndices(primaryCompiled.getVariablesTuple().size(), primaryIndices); + secondaryMask = TupleMask.fromSelectedIndices(callTrace.getVariablesTuple().size(), secondaryIndices); + complementerMask = TupleMask.fromSelectedIndices(callTrace.getVariablesTuple().size(), complementerIndices); + + primaryIndexer = makeIndexerTrace(planToCompile, primaryCompiled, primaryMask); + secondaryIndexer = makeIndexerTrace(planToCompile, callTrace, secondaryMask); + + naturalJoinRecipe = FACTORY.createJoinRecipe(); + naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); + naturalJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe()); + naturalJoinRecipe.setRightParentComplementaryMask(CompilerHelper.toRecipeMask(complementerMask)); + + naturalJoinVariablesTuple = new ArrayList(primaryCompiled.getVariablesTuple()); + for (int complementerIndex : complementerMask.indices) + naturalJoinVariablesTuple.add(callTrace.getVariablesTuple().get(complementerIndex)); + } + + public TupleMask getPrimaryMask() { + return primaryMask; + } + + public TupleMask getSecondaryMask() { + return secondaryMask; + } + + public TupleMask getComplementerMask() { + return complementerMask; + } + + public RecipeTraceInfo getPrimaryIndexer() { + return primaryIndexer; + } + + public RecipeTraceInfo getSecondaryIndexer() { + return secondaryIndexer; + } + + public JoinRecipe getNaturalJoinRecipe() { + return naturalJoinRecipe; + } + + public List getNaturalJoinVariablesTuple() { + return naturalJoinVariablesTuple; + } + + } + + /** + * @since 1.4 + */ + public static Mask toRecipeMask(TupleMask mask) { + return RecipesHelper.mask(mask.sourceWidth, mask.indices); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java new file mode 100644 index 00000000..7d1e4d3a --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/RecursionCutoffPoint.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.construction.plancompiler; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; +import tools.refinery.viatra.runtime.rete.recipes.ProductionRecipe; +import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; +import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; +import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; + +/** + * In a recursive query structure, query composition references can be cut off so that the remaining structure is DAG. + * {@link RecursionCutoffPoint} represents one such cut off query composition. + * When the compilation of the recursive query finishes and the compiled form becomes available, + * the {@link RecursionCutoffPoint} has to be signaled to update parent traces and recipes of the recursive call. + * + * @author Bergmann Gabor + * @noreference This class is not intended to be referenced by clients + * + */ +public class RecursionCutoffPoint { + final Map futureTraceMap; + final CompiledQuery compiledQuery; + final ProductionRecipe recipe; + final QueryEvaluationHint hint; + + public RecursionCutoffPoint(PQuery query, QueryEvaluationHint hint, IQueryMetaContext context, boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) { + super(); + this.hint = hint; + this.futureTraceMap = new HashMap<>(); // IMPORTANT: the identity of futureTraceMap.values() will not change + this.compiledQuery = CompilerHelper.makeQueryTrace(query, futureTraceMap, Collections.emptySet(), hint, context, deleteAndRederiveEvaluation, timelyEvaluation); + this.recipe = (ProductionRecipe)compiledQuery.getRecipe(); + if (!compiledQuery.getParentRecipeTraces().isEmpty()) { + throw new IllegalArgumentException(String.format("Recursion cut-off point of query %s has trace parents: %s", + compiledQuery.getQuery(), + prettyPrintParentRecipeTraces(compiledQuery.getParentRecipeTraces()))); + } + if (!recipe.getParents().isEmpty()) { + throw new IllegalArgumentException(String.format("Recursion cut-off point of query %s has recipe parents: %s", + compiledQuery.getQuery(), + prettyPrintParentRecipeTraces(compiledQuery.getParentRecipeTraces()))); + } + } + + + + private String prettyPrintParentRecipeTraces(List trace) { + return trace.stream().map(Object::toString).collect(Collectors.joining(", ")); + } + + /** + * Signals that compilation of the recursive query has terminated, culminating into the given compiled form. + * The query composition that has been cut off will be connected now. + */ + public void mend(CompiledQuery finalCompiledForm) { + futureTraceMap.putAll(finalCompiledForm.getParentRecipeTracesPerBody()); + recipe.getParents().addAll(((ProductionRecipe)finalCompiledForm.getRecipe()).getParents()); + } + + public CompiledQuery getCompiledQuery() { + return compiledQuery; + } + + public ProductionRecipe getRecipe() { + return recipe; + } + + + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java new file mode 100644 index 00000000..6dd270bd --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java @@ -0,0 +1,948 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.construction.plancompiler; + +import org.apache.log4j.Logger; +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.backend.CommonQueryHintOptions; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; +import tools.refinery.viatra.runtime.matchers.context.IQueryCacheContext; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper; +import tools.refinery.viatra.runtime.matchers.planning.operations.*; +import tools.refinery.viatra.runtime.matchers.psystem.*; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; +import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PVisibility; +import tools.refinery.viatra.runtime.matchers.psystem.rewriters.*; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; +import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper.JoinHelper; +import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper.PosetTriplet; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; +import tools.refinery.viatra.runtime.rete.recipes.*; +import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper; +import tools.refinery.viatra.runtime.rete.traceability.*; +import tools.refinery.viatra.runtime.rete.util.ReteHintOptions; + +import java.util.*; +import java.util.Map.Entry; +import java.util.function.BiFunction; +import java.util.function.Function; + +/** + * Compiles queries and query plans into Rete recipes, traced by respectively a {@link CompiledQuery} or + * {@link CompiledSubPlan}. + * + * @author Bergmann Gabor + * + */ +public class ReteRecipeCompiler { + + private final IQueryPlannerStrategy plannerStrategy; + private final IQueryMetaContext metaContext; + private final IQueryBackendHintProvider hintProvider; + private final PDisjunctionRewriter normalizer; + private final QueryAnalyzer queryAnalyzer; + private final Logger logger; + + /** + * @since 2.2 + */ + protected final boolean deleteAndRederiveEvaluation; + /** + * @since 2.4 + */ + protected final TimelyConfiguration timelyEvaluation; + + /** + * @since 1.5 + */ + public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext, + IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer) { + this(plannerStrategy, logger, metaContext, queryCacheContext, hintProvider, queryAnalyzer, false, null); + } + + /** + * @since 2.4 + */ + public ReteRecipeCompiler(IQueryPlannerStrategy plannerStrategy, Logger logger, IQueryMetaContext metaContext, + IQueryCacheContext queryCacheContext, IQueryBackendHintProvider hintProvider, QueryAnalyzer queryAnalyzer, + boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyEvaluation) { + super(); + this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation; + this.timelyEvaluation = timelyEvaluation; + this.plannerStrategy = plannerStrategy; + this.logger = logger; + this.metaContext = metaContext; + this.queryAnalyzer = queryAnalyzer; + this.normalizer = new PDisjunctionRewriterCacher(new SurrogateQueryRewriter(), + new PBodyNormalizer(metaContext) { + + @Override + protected boolean shouldExpandWeakenedAlternatives(PQuery query) { + QueryEvaluationHint hint = ReteRecipeCompiler.this.hintProvider.getQueryEvaluationHint(query); + Boolean expandWeakenedAlternativeConstraints = ReteHintOptions.expandWeakenedAlternativeConstraints + .getValueOrDefault(hint); + return expandWeakenedAlternativeConstraints; + } + + }); + this.hintProvider = hintProvider; + } + + static final RecipesFactory FACTORY = RecipesFactory.eINSTANCE; + + // INTERNALLY CACHED + private Map plannerCache = new HashMap(); + private Set planningInProgress = new HashSet(); + + private Map queryCompilerCache = new HashMap(); + private Set compilationInProgress = new HashSet(); + private IMultiLookup recursionCutoffPoints = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); + private Map subPlanCompilerCache = new HashMap(); + private Map compilerBackTrace = new HashMap(); + + /** + * Clears internal state + */ + public void reset() { + plannerCache.clear(); + planningInProgress.clear(); + queryCompilerCache.clear(); + subPlanCompilerCache.clear(); + compilerBackTrace.clear(); + } + + /** + * Returns a {@link CompiledQuery} compiled from a query + * @throws ViatraQueryRuntimeException + */ + public CompiledQuery getCompiledForm(PQuery query) { + CompiledQuery compiled = queryCompilerCache.get(query); + if (compiled == null) { + + IRewriterTraceCollector traceCollector = CommonQueryHintOptions.normalizationTraceCollector + .getValueOrDefault(hintProvider.getQueryEvaluationHint(query)); + if (traceCollector != null) { + traceCollector.addTrace(query, query); + } + + boolean reentrant = !compilationInProgress.add(query); + if (reentrant) { // oops, recursion into body in progress + RecursionCutoffPoint cutoffPoint = new RecursionCutoffPoint(query, getHints(query), metaContext, + deleteAndRederiveEvaluation, timelyEvaluation); + recursionCutoffPoints.addPair(query, cutoffPoint); + return cutoffPoint.getCompiledQuery(); + } else { // not reentrant, therefore no recursion, do the compilation + try { + compiled = compileProduction(query); + queryCompilerCache.put(query, compiled); + // backTrace.put(compiled.getRecipe(), plan); + + // if this was a recursive query, mend all points where recursion was cut off + for (RecursionCutoffPoint cutoffPoint : recursionCutoffPoints.lookupOrEmpty(query)) { + cutoffPoint.mend(compiled); + } + } finally { + compilationInProgress.remove(query); + } + } + } + return compiled; + } + + /** + * Returns a {@link CompiledSubPlan} compiled from a query plan + * @throws ViatraQueryRuntimeException + */ + public CompiledSubPlan getCompiledForm(SubPlan plan) { + CompiledSubPlan compiled = subPlanCompilerCache.get(plan); + if (compiled == null) { + compiled = doCompileDispatch(plan); + subPlanCompilerCache.put(plan, compiled); + compilerBackTrace.put(compiled.getRecipe(), plan); + } + return compiled; + } + + /** + * @throws ViatraQueryRuntimeException + */ + public SubPlan getPlan(PBody pBody) { + // if the query is not marked as being compiled, initiate compilation + // (this is useful in case of recursion if getPlan() is the entry point) + PQuery pQuery = pBody.getPattern(); + if (!compilationInProgress.contains(pQuery)) + getCompiledForm(pQuery); + + // Is the plan already cached? + SubPlan plan = plannerCache.get(pBody); + if (plan == null) { + boolean reentrant = !planningInProgress.add(pBody); + if (reentrant) { // oops, recursion into body in progress + throw new IllegalArgumentException( + "Planning-level recursion unsupported: " + pBody.getPattern().getFullyQualifiedName()); + } else { // not reentrant, therefore no recursion, do the planning + try { + plan = plannerStrategy.plan(pBody, logger, metaContext); + plannerCache.put(pBody, plan); + } finally { + planningInProgress.remove(pBody); + } + } + } + return plan; + } + + private CompiledQuery compileProduction(PQuery query) { + Collection bodyPlans = new ArrayList(); + normalizer.setTraceCollector(CommonQueryHintOptions.normalizationTraceCollector + .getValueOrDefault(hintProvider.getQueryEvaluationHint(query))); + for (PBody pBody : normalizer.rewrite(query).getBodies()) { + SubPlan bodyPlan = getPlan(pBody); + bodyPlans.add(bodyPlan); + } + return doCompileProduction(query, bodyPlans); + } + + private CompiledQuery doCompileProduction(PQuery query, Collection bodies) { + // TODO skip production node if there is just one body and no projection needed? + Map bodyFinalTraces = new HashMap(); + Collection bodyFinalRecipes = new HashSet(); + + for (SubPlan bodyFinalPlan : bodies) { + // skip over any projections at the end + bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan); + + // TODO checkAndTrimEqualVariables may introduce superfluous trim, + // but whatever (no uniqueness enforcer needed) + + // compile body + final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan); + + // project to parameter list + RecipeTraceInfo finalTrace = projectBodyFinalToParameters(compiledBody, false); + + bodyFinalTraces.put(bodyFinalPlan.getBody(), finalTrace); + bodyFinalRecipes.add(finalTrace.getRecipe()); + } + + CompiledQuery compiled = CompilerHelper.makeQueryTrace(query, bodyFinalTraces, bodyFinalRecipes, + getHints(query), metaContext, deleteAndRederiveEvaluation, timelyEvaluation); + + return compiled; + } + + private CompiledSubPlan doCompileDispatch(SubPlan plan) { + final POperation operation = plan.getOperation(); + if (operation instanceof PEnumerate) { + return doCompileEnumerate(((PEnumerate) operation).getEnumerablePConstraint(), plan); + } else if (operation instanceof PApply) { + final PConstraint pConstraint = ((PApply) operation).getPConstraint(); + if (pConstraint instanceof EnumerablePConstraint) { + CompiledSubPlan primaryParent = getCompiledForm(plan.getParentPlans().get(0)); + PlanningTrace secondaryParent = doEnumerateDispatch(plan, (EnumerablePConstraint) pConstraint); + return compileToNaturalJoin(plan, primaryParent, secondaryParent); + } else if (pConstraint instanceof DeferredPConstraint) { + return doDeferredDispatch((DeferredPConstraint) pConstraint, plan); + } else { + throw new IllegalArgumentException("Unsupported PConstraint in query plan: " + plan.toShortString()); + } + } else if (operation instanceof PJoin) { + return doCompileJoin((PJoin) operation, plan); + } else if (operation instanceof PProject) { + return doCompileProject((PProject) operation, plan); + } else if (operation instanceof PStart) { + return doCompileStart((PStart) operation, plan); + } else { + throw new IllegalArgumentException("Unsupported POperation in query plan: " + plan.toShortString()); + } + } + + private CompiledSubPlan doDeferredDispatch(DeferredPConstraint constraint, SubPlan plan) { + final SubPlan parentPlan = plan.getParentPlans().get(0); + final CompiledSubPlan parentCompiled = getCompiledForm(parentPlan); + if (constraint instanceof Equality) { + return compileDeferred((Equality) constraint, plan, parentPlan, parentCompiled); + } else if (constraint instanceof ExportedParameter) { + return compileDeferred((ExportedParameter) constraint, plan, parentPlan, parentCompiled); + } else if (constraint instanceof Inequality) { + return compileDeferred((Inequality) constraint, plan, parentPlan, parentCompiled); + } else if (constraint instanceof NegativePatternCall) { + return compileDeferred((NegativePatternCall) constraint, plan, parentPlan, parentCompiled); + } else if (constraint instanceof PatternMatchCounter) { + return compileDeferred((PatternMatchCounter) constraint, plan, parentPlan, parentCompiled); + } else if (constraint instanceof AggregatorConstraint) { + return compileDeferred((AggregatorConstraint) constraint, plan, parentPlan, parentCompiled); + } else if (constraint instanceof ExpressionEvaluation) { + return compileDeferred((ExpressionEvaluation) constraint, plan, parentPlan, parentCompiled); + } else if (constraint instanceof TypeFilterConstraint) { + return compileDeferred((TypeFilterConstraint) constraint, plan, parentPlan, parentCompiled); + } + throw new UnsupportedOperationException("Unknown deferred constraint " + constraint); + } + + private CompiledSubPlan compileDeferred(Equality constraint, SubPlan plan, SubPlan parentPlan, + CompiledSubPlan parentCompiled) { + if (constraint.isMoot()) + return parentCompiled.cloneFor(plan); + + Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho()); + Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom()); + + if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) { + Integer indexLower = Math.min(index1, index2); + Integer indexHigher = Math.max(index1, index2); + + EqualityFilterRecipe equalityFilterRecipe = FACTORY.createEqualityFilterRecipe(); + equalityFilterRecipe.setParent(parentCompiled.getRecipe()); + equalityFilterRecipe.getIndices().add(indexLower); + equalityFilterRecipe.getIndices().add(indexHigher); + + return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), equalityFilterRecipe, parentCompiled); + } else { + throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s", + plan.toShortString(), parentCompiled.toString())); + } + } + + /** + * Precondition: constantTrace must map to a ConstantRecipe, and all of its variables must be contained in + * toFilterTrace. + */ + private CompiledSubPlan compileConstantFiltering(SubPlan plan, PlanningTrace toFilterTrace, + ConstantRecipe constantRecipe, List filteredVariables) { + PlanningTrace resultTrace = toFilterTrace; + + int constantVariablesSize = filteredVariables.size(); + for (int i = 0; i < constantVariablesSize; ++i) { + Object constantValue = constantRecipe.getConstantValues().get(i); + PVariable filteredVariable = filteredVariables.get(i); + int filteredColumn = resultTrace.getVariablesTuple().indexOf(filteredVariable); + + DiscriminatorDispatcherRecipe dispatcherRecipe = FACTORY.createDiscriminatorDispatcherRecipe(); + dispatcherRecipe.setDiscriminationColumnIndex(filteredColumn); + dispatcherRecipe.setParent(resultTrace.getRecipe()); + + PlanningTrace dispatcherTrace = new PlanningTrace(plan, resultTrace.getVariablesTuple(), dispatcherRecipe, + resultTrace); + + DiscriminatorBucketRecipe bucketRecipe = FACTORY.createDiscriminatorBucketRecipe(); + bucketRecipe.setBucketKey(constantValue); + bucketRecipe.setParent(dispatcherRecipe); + + PlanningTrace bucketTrace = new PlanningTrace(plan, dispatcherTrace.getVariablesTuple(), bucketRecipe, + dispatcherTrace); + + resultTrace = bucketTrace; + } + + return resultTrace.cloneFor(plan); + } + + private CompiledSubPlan compileDeferred(ExportedParameter constraint, SubPlan plan, SubPlan parentPlan, + CompiledSubPlan parentCompiled) { + return parentCompiled.cloneFor(plan); + } + + private CompiledSubPlan compileDeferred(Inequality constraint, SubPlan plan, SubPlan parentPlan, + CompiledSubPlan parentCompiled) { + if (constraint.isEliminable()) + return parentCompiled.cloneFor(plan); + + Integer index1 = parentCompiled.getPosMapping().get(constraint.getWho()); + Integer index2 = parentCompiled.getPosMapping().get(constraint.getWithWhom()); + + if (index1 != null && index2 != null && index1.intValue() != index2.intValue()) { + Integer indexLower = Math.min(index1, index2); + Integer indexHigher = Math.max(index1, index2); + + InequalityFilterRecipe inequalityFilterRecipe = FACTORY.createInequalityFilterRecipe(); + inequalityFilterRecipe.setParent(parentCompiled.getRecipe()); + inequalityFilterRecipe.setSubject(indexLower); + inequalityFilterRecipe.getInequals().add(indexHigher); + + return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), inequalityFilterRecipe, + parentCompiled); + } else { + throw new IllegalArgumentException(String.format("Unable to interpret %s after compiled parent %s", + plan.toShortString(), parentCompiled.toString())); + } + } + + private CompiledSubPlan compileDeferred(TypeFilterConstraint constraint, SubPlan plan, SubPlan parentPlan, + CompiledSubPlan parentCompiled) { + final IInputKey inputKey = constraint.getInputKey(); + if (!metaContext.isStateless(inputKey)) + throw new UnsupportedOperationException( + "Non-enumerable input keys are currently supported in Rete only if they are stateless, unlike " + + inputKey); + + final Tuple constraintVariables = constraint.getVariablesTuple(); + final List parentVariables = parentCompiled.getVariablesTuple(); + + Mask mask; // select elements of the tuple to check against extensional relation + if (Tuples.flatTupleOf(parentVariables.toArray()).equals(constraintVariables)) { + mask = null; // lucky case, parent signature equals that of input key + } else { + List variables = new ArrayList(); + for (Object variable : constraintVariables.getElements()) { + variables.add((PVariable) variable); + } + mask = CompilerHelper.makeProjectionMask(parentCompiled, variables); + } + InputFilterRecipe inputFilterRecipe = RecipesHelper.inputFilterRecipe(parentCompiled.getRecipe(), inputKey, + inputKey.getStringID(), mask); + return new CompiledSubPlan(plan, parentVariables, inputFilterRecipe, parentCompiled); + } + + private CompiledSubPlan compileDeferred(NegativePatternCall constraint, SubPlan plan, SubPlan parentPlan, + CompiledSubPlan parentCompiled) { + final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, + constraint.getActualParametersTuple()); + + JoinHelper joinHelper = new JoinHelper(plan, parentCompiled, callTrace); + final RecipeTraceInfo primaryIndexer = joinHelper.getPrimaryIndexer(); + final RecipeTraceInfo secondaryIndexer = joinHelper.getSecondaryIndexer(); + + AntiJoinRecipe antiJoinRecipe = FACTORY.createAntiJoinRecipe(); + antiJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); + antiJoinRecipe.setRightParent((IndexerRecipe) secondaryIndexer.getRecipe()); + + return new CompiledSubPlan(plan, parentCompiled.getVariablesTuple(), antiJoinRecipe, primaryIndexer, + secondaryIndexer); + } + + private CompiledSubPlan compileDeferred(PatternMatchCounter constraint, SubPlan plan, SubPlan parentPlan, + CompiledSubPlan parentCompiled) { + final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, + constraint.getActualParametersTuple()); + + // hack: use some mask computations (+ the indexers) from a fake natural join against the called query + JoinHelper fakeJoinHelper = new JoinHelper(plan, parentCompiled, callTrace); + final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer(); + final RecipeTraceInfo callProjectionIndexer = fakeJoinHelper.getSecondaryIndexer(); + + final List sideVariablesTuple = new ArrayList( + fakeJoinHelper.getSecondaryMask().transform(callTrace.getVariablesTuple())); + /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable()); + + CountAggregatorRecipe aggregatorRecipe = FACTORY.createCountAggregatorRecipe(); + aggregatorRecipe.setParent((ProjectionIndexerRecipe) callProjectionIndexer.getRecipe()); + PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe, + callProjectionIndexer); + + IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe(); + aggregatorIndexerRecipe.setParent(aggregatorRecipe); + // aggregatorIndexerRecipe.setMask(RecipesHelper.mask( + // sideVariablesTuple.size(), + // //use same indices as in the projection indexer + // // EVEN if result variable already visible in left parent + // fakeJoinHelper.getSecondaryMask().indices + // )); + + int aggregatorWidth = sideVariablesTuple.size(); + int aggregateResultIndex = aggregatorWidth - 1; + + aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit( + // aggregate according all but the last index + aggregateResultIndex, aggregatorWidth))); + PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe, + aggregatorTrace); + + JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe(); + naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); + naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe); + naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth, + // extend with last element only - the computation value + aggregateResultIndex)); + + // what if the new variable already has a value? + // even if already known, we add the new result variable, so that it can be filtered at the end + // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable()); + + final List aggregatedVariablesTuple = new ArrayList(parentCompiled.getVariablesTuple()); + aggregatedVariablesTuple.add(constraint.getResultVariable()); + + PlanningTrace joinTrace = new PlanningTrace(plan, aggregatedVariablesTuple, naturalJoinRecipe, primaryIndexer, + aggregatorIndexerTrace); + + return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan); + // if (!alreadyKnown) { + // return joinTrace.cloneFor(plan); + // } else { + // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple()); + // } + } + + private CompiledSubPlan compileDeferred(AggregatorConstraint constraint, SubPlan plan, SubPlan parentPlan, + CompiledSubPlan parentCompiled) { + final PlanningTrace callTrace = referQuery(constraint.getReferredQuery(), plan, + constraint.getActualParametersTuple()); + + // hack: use some mask computations (+ the indexers) from a fake natural join against the called query + JoinHelper fakeJoinHelper = new JoinHelper(plan, parentCompiled, callTrace); + final RecipeTraceInfo primaryIndexer = fakeJoinHelper.getPrimaryIndexer(); + TupleMask callGroupMask = fakeJoinHelper.getSecondaryMask(); + + final List sideVariablesTuple = new ArrayList( + callGroupMask.transform(callTrace.getVariablesTuple())); + /* if (!booleanCheck) */ sideVariablesTuple.add(constraint.getResultVariable()); + + IMultisetAggregationOperator operator = constraint.getAggregator().getOperator(); + + SingleColumnAggregatorRecipe columnAggregatorRecipe = FACTORY.createSingleColumnAggregatorRecipe(); + columnAggregatorRecipe.setParent(callTrace.getRecipe()); + columnAggregatorRecipe.setMultisetAggregationOperator(operator); + + int columnIndex = constraint.getAggregatedColumn(); + IPosetComparator posetComparator = null; + Mask groupMask = CompilerHelper.toRecipeMask(callGroupMask); + + // temporary solution to support the deprecated option for now + final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(plan)); + + columnAggregatorRecipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); + if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) { + List parameters = constraint.getReferredQuery().getParameters(); + IInputKey key = parameters.get(columnIndex).getDeclaredUnaryType(); + if (key != null && metaContext.isPosetKey(key)) { + posetComparator = metaContext.getPosetComparator(Collections.singleton(key)); + } + } + + if (posetComparator == null) { + columnAggregatorRecipe.setGroupByMask(groupMask); + columnAggregatorRecipe.setAggregableIndex(columnIndex); + } else { + MonotonicityInfo monotonicityInfo = FACTORY.createMonotonicityInfo(); + monotonicityInfo.setCoreMask(groupMask); + monotonicityInfo.setPosetMask(CompilerHelper.toRecipeMask( + TupleMask.selectSingle(columnIndex, constraint.getActualParametersTuple().getSize()))); + monotonicityInfo.setPosetComparator(posetComparator); + columnAggregatorRecipe.setOptionalMonotonicityInfo(monotonicityInfo); + } + + ReteNodeRecipe aggregatorRecipe = columnAggregatorRecipe; + PlanningTrace aggregatorTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorRecipe, callTrace); + + IndexerRecipe aggregatorIndexerRecipe = FACTORY.createAggregatorIndexerRecipe(); + aggregatorIndexerRecipe.setParent(aggregatorRecipe); + + int aggregatorWidth = sideVariablesTuple.size(); + int aggregateResultIndex = aggregatorWidth - 1; + + aggregatorIndexerRecipe.setMask(CompilerHelper.toRecipeMask(TupleMask.omit( + // aggregate according all but the last index + aggregateResultIndex, aggregatorWidth))); + PlanningTrace aggregatorIndexerTrace = new PlanningTrace(plan, sideVariablesTuple, aggregatorIndexerRecipe, + aggregatorTrace); + + JoinRecipe naturalJoinRecipe = FACTORY.createJoinRecipe(); + naturalJoinRecipe.setLeftParent((ProjectionIndexerRecipe) primaryIndexer.getRecipe()); + naturalJoinRecipe.setRightParent(aggregatorIndexerRecipe); + naturalJoinRecipe.setRightParentComplementaryMask(RecipesHelper.mask(aggregatorWidth, + // extend with last element only - the computation value + aggregateResultIndex)); + + // what if the new variable already has a value? + // even if already known, we add the new result variable, so that it can be filtered at the end + // boolean alreadyKnown = parentPlan.getVisibleVariables().contains(constraint.getResultVariable()); + + final List finalVariablesTuple = new ArrayList(parentCompiled.getVariablesTuple()); + finalVariablesTuple.add(constraint.getResultVariable()); + + PlanningTrace joinTrace = new PlanningTrace(plan, finalVariablesTuple, naturalJoinRecipe, primaryIndexer, + aggregatorIndexerTrace); + + return CompilerHelper.checkAndTrimEqualVariables(plan, joinTrace).cloneFor(plan); + // if (!alreadyKnown) { + // return joinTrace.cloneFor(plan); + // } else { + // //final Integer equalsWithIndex = parentCompiled.getPosMapping().get(parentCompiled.getVariablesTuple()); + // } + } + + private CompiledSubPlan compileDeferred(ExpressionEvaluation constraint, SubPlan plan, SubPlan parentPlan, + CompiledSubPlan parentCompiled) { + Map tupleNameMap = new HashMap(); + for (String name : constraint.getEvaluator().getInputParameterNames()) { + Map index = parentCompiled.getPosMapping(); + PVariable variable = constraint.getPSystem().getVariableByNameChecked(name); + Integer position = index.get(variable); + tupleNameMap.put(name, position); + } + + final PVariable outputVariable = constraint.getOutputVariable(); + final boolean booleanCheck = outputVariable == null; + + // TODO determine whether expression is costly + boolean cacheOutput = ReteHintOptions.cacheOutputOfEvaluatorsByDefault.getValueOrDefault(getHints(plan)); + // for (PAnnotation pAnnotation : + // plan.getBody().getPattern().getAnnotationsByName(EXPRESSION_EVALUATION_ANNOTATION"")) { + // for (Object value : pAnnotation.getAllValues("expensive")) { + // if (value instanceof Boolean) + // cacheOutput = (boolean) value; + // } + // } + + ExpressionEnforcerRecipe enforcerRecipe = booleanCheck ? FACTORY.createCheckRecipe() + : FACTORY.createEvalRecipe(); + enforcerRecipe.setParent(parentCompiled.getRecipe()); + enforcerRecipe.setExpression(RecipesHelper.expressionDefinition(constraint.getEvaluator())); + enforcerRecipe.setCacheOutput(cacheOutput); + if (enforcerRecipe instanceof EvalRecipe) { + ((EvalRecipe) enforcerRecipe).setUnwinding(constraint.isUnwinding()); + } + for (Entry entry : tupleNameMap.entrySet()) { + enforcerRecipe.getMappedIndices().put(entry.getKey(), entry.getValue()); + } + + final List enforcerVariablesTuple = new ArrayList(parentCompiled.getVariablesTuple()); + if (!booleanCheck) + enforcerVariablesTuple.add(outputVariable); + PlanningTrace enforcerTrace = new PlanningTrace(plan, enforcerVariablesTuple, enforcerRecipe, parentCompiled); + + return CompilerHelper.checkAndTrimEqualVariables(plan, enforcerTrace).cloneFor(plan); + } + + private CompiledSubPlan doCompileJoin(PJoin operation, SubPlan plan) { + final List compiledParents = getCompiledFormOfParents(plan); + final CompiledSubPlan leftCompiled = compiledParents.get(0); + final CompiledSubPlan rightCompiled = compiledParents.get(1); + + return compileToNaturalJoin(plan, leftCompiled, rightCompiled); + } + + private CompiledSubPlan compileToNaturalJoin(SubPlan plan, final PlanningTrace leftCompiled, + final PlanningTrace rightCompiled) { + // CHECK IF SPECIAL CASE + + // Is constant filtering applicable? + if (ReteHintOptions.useDiscriminatorDispatchersForConstantFiltering.getValueOrDefault(getHints(plan))) { + if (leftCompiled.getRecipe() instanceof ConstantRecipe + && rightCompiled.getVariablesTuple().containsAll(leftCompiled.getVariablesTuple())) { + return compileConstantFiltering(plan, rightCompiled, (ConstantRecipe) leftCompiled.getRecipe(), + leftCompiled.getVariablesTuple()); + } + if (rightCompiled.getRecipe() instanceof ConstantRecipe + && leftCompiled.getVariablesTuple().containsAll(rightCompiled.getVariablesTuple())) { + return compileConstantFiltering(plan, leftCompiled, (ConstantRecipe) rightCompiled.getRecipe(), + rightCompiled.getVariablesTuple()); + } + } + + // ELSE: ACTUAL JOIN + JoinHelper joinHelper = new JoinHelper(plan, leftCompiled, rightCompiled); + return new CompiledSubPlan(plan, joinHelper.getNaturalJoinVariablesTuple(), joinHelper.getNaturalJoinRecipe(), + joinHelper.getPrimaryIndexer(), joinHelper.getSecondaryIndexer()); + } + + private CompiledSubPlan doCompileProject(PProject operation, SubPlan plan) { + final List compiledParents = getCompiledFormOfParents(plan); + final CompiledSubPlan compiledParent = compiledParents.get(0); + + List projectedVariables = new ArrayList(operation.getToVariables()); + // Determinizing projection: try to keep original order (hopefully facilitates node reuse) + Map parentPosMapping = compiledParent.getPosMapping(); + Collections.sort(projectedVariables, Comparator.comparing(parentPosMapping::get)); + + return doProjectPlan(compiledParent, projectedVariables, true, + parentTrace -> parentTrace.cloneFor(plan), + (recipe, parentTrace) -> new PlanningTrace(plan, projectedVariables, recipe, parentTrace), + (recipe, parentTrace) -> new CompiledSubPlan(plan, projectedVariables, recipe, parentTrace) + ); + } + + /** + * Projects a subplan onto the specified variable tuple + * @param compiledParentPlan the compiled form of the subplan + * @param targetVariables list of variables to project to + * @param enforceUniqueness whether distinctness shall be enforced after the projection. + * Specify false only if directly connecting to a production node. + * @param reinterpretTraceFactory constructs a reinterpreted trace that simply relabels the compiled parent plan, in case it is sufficient + * @param intermediateTraceFactory constructs a recipe trace for an intermediate node, given the recipe of the node and its parent trace + * @param finalTraceFactory constructs a recipe trace for the final resulting node, given the recipe of the node and its parent trace + * @since 2.1 + */ + ResultTrace doProjectPlan( + final CompiledSubPlan compiledParentPlan, + final List targetVariables, + boolean enforceUniqueness, + Function reinterpretTraceFactory, + BiFunction intermediateTraceFactory, + BiFunction finalTraceFactory) + { + if (targetVariables.equals(compiledParentPlan.getVariablesTuple())) // no projection needed + return reinterpretTraceFactory.apply(compiledParentPlan); + + // otherwise, we need at least a trimmer + TrimmerRecipe trimmerRecipe = CompilerHelper.makeTrimmerRecipe(compiledParentPlan, targetVariables); + + // do we need to eliminate duplicates? + SubPlan parentPlan = compiledParentPlan.getSubPlan(); + if (!enforceUniqueness || BuildHelper.areAllVariablesDetermined( + parentPlan, + targetVariables, + queryAnalyzer, + true)) + { + // if uniqueness enforcess is unwanted or unneeeded, skip it + return finalTraceFactory.apply(trimmerRecipe, compiledParentPlan); + } else { + // add a uniqueness enforcer + UniquenessEnforcerRecipe recipe = FACTORY.createUniquenessEnforcerRecipe(); + recipe.getParents().add(trimmerRecipe); + + // temporary solution to support the deprecated option for now + final boolean deleteAndRederiveEvaluationDep = this.deleteAndRederiveEvaluation || ReteHintOptions.deleteRederiveEvaluation.getValueOrDefault(getHints(parentPlan)); + + recipe.setDeleteRederiveEvaluation(deleteAndRederiveEvaluationDep); + if (deleteAndRederiveEvaluationDep || (this.timelyEvaluation != null)) { + PosetTriplet triplet = CompilerHelper.computePosetInfo(targetVariables, parentPlan.getBody(), metaContext); + + if (triplet.comparator != null) { + MonotonicityInfo info = FACTORY.createMonotonicityInfo(); + info.setCoreMask(triplet.coreMask); + info.setPosetMask(triplet.posetMask); + info.setPosetComparator(triplet.comparator); + recipe.setOptionalMonotonicityInfo(info); + } + } + + RecipeTraceInfo trimmerTrace = intermediateTraceFactory.apply(trimmerRecipe, compiledParentPlan); + return finalTraceFactory.apply(recipe, trimmerTrace); + } + } + + /** + * Projects the final compiled form of a PBody onto the parameter tuple + * @param compiledBody the compiled form of the body, with all constraints enforced, not yet projected to query parameters + * @param enforceUniqueness whether distinctness shall be enforced after the projection. + * Specify false only if directly connecting to a production node. + * @since 2.1 + */ + RecipeTraceInfo projectBodyFinalToParameters( + final CompiledSubPlan compiledBody, + boolean enforceUniqueness) + { + final PBody body = compiledBody.getSubPlan().getBody(); + final List parameterList = body.getSymbolicParameterVariables(); + + return doProjectPlan(compiledBody, parameterList, enforceUniqueness, + parentTrace -> parentTrace, + (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace), + (recipe, parentTrace) -> new ParameterProjectionTrace(body, recipe, parentTrace) + ); + } + + private CompiledSubPlan doCompileStart(PStart operation, SubPlan plan) { + if (!operation.getAPrioriVariables().isEmpty()) { + throw new IllegalArgumentException("Input variables unsupported by Rete: " + plan.toShortString()); + } + final ConstantRecipe recipe = FACTORY.createConstantRecipe(); + recipe.getConstantValues().clear(); + + return new CompiledSubPlan(plan, new ArrayList(), recipe); + } + + private CompiledSubPlan doCompileEnumerate(EnumerablePConstraint constraint, SubPlan plan) { + final PlanningTrace trimmedTrace = doEnumerateAndDeduplicate(constraint, plan); + + return trimmedTrace.cloneFor(plan); + } + + private PlanningTrace doEnumerateAndDeduplicate(EnumerablePConstraint constraint, SubPlan plan) { + final PlanningTrace coreTrace = doEnumerateDispatch(plan, constraint); + final PlanningTrace trimmedTrace = CompilerHelper.checkAndTrimEqualVariables(plan, coreTrace); + return trimmedTrace; + } + + private PlanningTrace doEnumerateDispatch(SubPlan plan, EnumerablePConstraint constraint) { + if (constraint instanceof RelationEvaluation) { + return compileEnumerable(plan, (RelationEvaluation) constraint); + } else if (constraint instanceof BinaryTransitiveClosure) { + return compileEnumerable(plan, (BinaryTransitiveClosure) constraint); + } else if (constraint instanceof BinaryReflexiveTransitiveClosure) { + return compileEnumerable(plan, (BinaryReflexiveTransitiveClosure) constraint); + } else if (constraint instanceof RepresentativeElectionConstraint) { + return compileEnumerable(plan, (RepresentativeElectionConstraint) constraint); + } else if (constraint instanceof ConstantValue) { + return compileEnumerable(plan, (ConstantValue) constraint); + } else if (constraint instanceof PositivePatternCall) { + return compileEnumerable(plan, (PositivePatternCall) constraint); + } else if (constraint instanceof TypeConstraint) { + return compileEnumerable(plan, (TypeConstraint) constraint); + } + throw new UnsupportedOperationException("Unknown enumerable constraint " + constraint); + } + + private PlanningTrace compileEnumerable(SubPlan plan, BinaryReflexiveTransitiveClosure constraint) { + // TODO the implementation would perform better if an inequality check would be used after tcRecipe and + // uniqueness enforcer be replaced by a transparent node with multiple parents, but such a node is not available + // in recipe metamodel in VIATRA 2.0 + + // Find called query + final PQuery referredQuery = constraint.getSupplierKey(); + final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); + + // Calculate irreflexive transitive closure + final TransitiveClosureRecipe tcRecipe = FACTORY.createTransitiveClosureRecipe(); + tcRecipe.setParent(callTrace.getRecipe()); + final PlanningTrace tcTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), tcRecipe, callTrace); + + // Enumerate universe type + final IInputKey inputKey = constraint.getUniverseType(); + final InputRecipe universeTypeRecipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity()); + final PlanningTrace universeTypeTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple( + Tuples.staticArityFlatTupleOf(constraint.getVariablesTuple().get(0))), universeTypeRecipe); + + // Calculate reflexive access by duplicating universe type column + final TrimmerRecipe reflexiveRecipe = FACTORY.createTrimmerRecipe(); + reflexiveRecipe.setMask(RecipesHelper.mask(1, 0, 0)); + reflexiveRecipe.setParent(universeTypeRecipe); + final PlanningTrace reflexiveTrace = new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), reflexiveRecipe, universeTypeTrace); + + // Finally, reduce duplicates after a join + final UniquenessEnforcerRecipe brtcRecipe = FACTORY.createUniquenessEnforcerRecipe(); + brtcRecipe.getParents().add(tcRecipe); + brtcRecipe.getParents().add(reflexiveRecipe); + + return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), brtcRecipe, reflexiveTrace, tcTrace); + } + + private PlanningTrace compileEnumerable(SubPlan plan, RepresentativeElectionConstraint constraint) { + var referredQuery = constraint.getSupplierKey(); + var callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); + var recipe = FACTORY.createRepresentativeElectionRecipe(); + recipe.setParent(callTrace.getRecipe()); + recipe.setConnectivity(constraint.getConnectivity()); + return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace); + } + + private PlanningTrace compileEnumerable(SubPlan plan, BinaryTransitiveClosure constraint) { + final PQuery referredQuery = constraint.getSupplierKey(); + final PlanningTrace callTrace = referQuery(referredQuery, plan, constraint.getVariablesTuple()); + + final TransitiveClosureRecipe recipe = FACTORY.createTransitiveClosureRecipe(); + recipe.setParent(callTrace.getRecipe()); + + return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, callTrace); + } + + private PlanningTrace compileEnumerable(SubPlan plan, RelationEvaluation constraint) { + final List parentRecipes = new ArrayList(); + final List parentTraceInfos = new ArrayList(); + for (final PQuery inputQuery : constraint.getReferredQueries()) { + final CompiledQuery compiledQuery = getCompiledForm(inputQuery); + parentRecipes.add(compiledQuery.getRecipe()); + parentTraceInfos.add(compiledQuery); + } + final RelationEvaluationRecipe recipe = FACTORY.createRelationEvaluationRecipe(); + recipe.getParents().addAll(parentRecipes); + recipe.setEvaluator(RecipesHelper.expressionDefinition(constraint.getEvaluator())); + return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe, parentTraceInfos); + } + + private PlanningTrace compileEnumerable(SubPlan plan, PositivePatternCall constraint) { + final PQuery referredQuery = constraint.getReferredQuery(); + return referQuery(referredQuery, plan, constraint.getVariablesTuple()); + } + + private PlanningTrace compileEnumerable(SubPlan plan, TypeConstraint constraint) { + final IInputKey inputKey = constraint.getSupplierKey(); + final InputRecipe recipe = RecipesHelper.inputRecipe(inputKey, inputKey.getStringID(), inputKey.getArity()); + return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe); + } + + private PlanningTrace compileEnumerable(SubPlan plan, ConstantValue constraint) { + final ConstantRecipe recipe = FACTORY.createConstantRecipe(); + recipe.getConstantValues().add(constraint.getSupplierKey()); + return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(constraint), recipe); + } + + // TODO handle recursion + private PlanningTrace referQuery(PQuery query, SubPlan plan, Tuple actualParametersTuple) { + RecipeTraceInfo referredQueryTrace = originalTraceOfReferredQuery(query); + return new PlanningTrace(plan, CompilerHelper.convertVariablesTuple(actualParametersTuple), + referredQueryTrace.getRecipe(), referredQueryTrace.getParentRecipeTracesForCloning()); + } + + private RecipeTraceInfo originalTraceOfReferredQuery(PQuery query) { + // eliminate superfluous production node? + if (PVisibility.EMBEDDED == query.getVisibility()) { // currently inline patterns only + Set rewrittenBodies = normalizer.rewrite(query).getBodies(); + if (1 == rewrittenBodies.size()) { // non-disjunctive + // TODO in the future, check if non-recursive - (not currently permitted) + + PBody pBody = rewrittenBodies.iterator().next(); + SubPlan bodyFinalPlan = getPlan(pBody); + + // skip over any projections at the end + bodyFinalPlan = BuildHelper.eliminateTrailingProjections(bodyFinalPlan); + + // TODO checkAndTrimEqualVariables may introduce superfluous trim, + // but whatever (no uniqueness enforcer needed) + + // compile body + final CompiledSubPlan compiledBody = getCompiledForm(bodyFinalPlan); + + // project to parameter list, add uniqueness enforcer if necessary + return projectBodyFinalToParameters(compiledBody, true /* ensure uniqueness, as no production node is used */); + } + } + + // otherwise, regular reference to recipe realizing the query + return getCompiledForm(query); + } + + protected List getCompiledFormOfParents(SubPlan plan) { + List results = new ArrayList(); + for (SubPlan parentPlan : plan.getParentPlans()) { + results.add(getCompiledForm(parentPlan)); + } + return results; + } + + /** + * Returns an unmodifiable view of currently cached compiled queries. + */ + public Map getCachedCompiledQueries() { + return Collections.unmodifiableMap(queryCompilerCache); + } + + /** + * Returns an unmodifiable view of currently cached query plans. + */ + public Map getCachedQueryPlans() { + return Collections.unmodifiableMap(plannerCache); + } + + private QueryEvaluationHint getHints(SubPlan plan) { + return getHints(plan.getBody().getPattern()); + } + + private QueryEvaluationHint getHints(PQuery pattern) { + return hintProvider.getQueryEvaluationHint(pattern); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java new file mode 100644 index 00000000..45350099 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinCandidate.java @@ -0,0 +1,162 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.construction.quasitree; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; +import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper; +import tools.refinery.viatra.runtime.matchers.planning.operations.PJoin; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; + +/** + * @author Gabor Bergmann + * + */ +class JoinCandidate { + private QueryAnalyzer analyzer; + + SubPlan primary; + SubPlan secondary; + + Set varPrimary; + Set varSecondary; + Set varCommon; + + List consPrimary; + List consSecondary; + + + JoinCandidate(SubPlan primary, SubPlan secondary, QueryAnalyzer analyzer) { + super(); + this.primary = primary; + this.secondary = secondary; + this.analyzer = analyzer; + + varPrimary = getPrimary().getVisibleVariables(); + varSecondary = getSecondary().getVisibleVariables(); + varCommon = CollectionsFactory.createSet(varPrimary); + varCommon.retainAll(varSecondary); + + consPrimary = new ArrayList(primary.getAllEnforcedConstraints()); + Collections.sort(consPrimary, TieBreaker.CONSTRAINT_COMPARATOR); + consSecondary = new ArrayList(secondary.getAllEnforcedConstraints()); + Collections.sort(consSecondary, TieBreaker.CONSTRAINT_COMPARATOR); + } + + + + /** + * @return the a + */ + public SubPlan getPrimary() { + return primary; + } + + /** + * @return the b + */ + public SubPlan getSecondary() { + return secondary; + } + + public SubPlan getJoinedPlan(SubPlanFactory factory) { + // check special cases first + if (isTrivial()) + return primary; + if (isSubsumption()) + return + (consPrimary.size() > consSecondary.size()) ? primary : secondary; + + + // default case + return factory.createSubPlan(new PJoin(), primary, secondary); + } + + @Override + public String toString() { + return primary.toString() + " |x| " + secondary.toString(); + } + + /** + * @return the varPrimary + */ + public Set getVarPrimary() { + return varPrimary; + } + + /** + * @return the varSecondary + */ + public Set getVarSecondary() { + return varSecondary; + } + + /** + * @return constraints of primary, sorted according to {@link TieBreaker#CONSTRAINT_COMPARATOR}. + */ + public List getConsPrimary() { + return consPrimary; + } + /** + * @return constraints of secondary, sorted according to {@link TieBreaker#CONSTRAINT_COMPARATOR}. + */ + public List getConsSecondary() { + return consSecondary; + } + + + + public boolean isTrivial() { + return getPrimary().equals(getSecondary()); + } + + public boolean isSubsumption() { + return consPrimary.containsAll(consSecondary) || consSecondary.containsAll(consPrimary); + } + + public boolean isCheckOnly() { + return varPrimary.containsAll(varSecondary) || varSecondary.containsAll(varPrimary); + } + + public boolean isDescartes() { + return Collections.disjoint(varPrimary, varSecondary); + } + + private Boolean heath; + + // it is a Heath-join iff common variables functionally determine either all primary or all secondary variables + public boolean isHeath() { + if (heath == null) { + Set union = Stream.concat( + primary.getAllEnforcedConstraints().stream(), + secondary.getAllEnforcedConstraints().stream() + ).collect(Collectors.toSet()); + Map, Set> dependencies = + analyzer.getFunctionalDependencies(union, false); + // does varCommon determine either varPrimary or varSecondary? + Set varCommonClosure = FunctionalDependencyHelper.closureOf(varCommon, dependencies); + + heath = varCommonClosure.containsAll(varPrimary) || varCommonClosure.containsAll(varSecondary); + } + return heath; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java new file mode 100644 index 00000000..0ea7c1d9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/JoinOrderingHeuristics.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.construction.quasitree; + +import java.util.Comparator; + +import tools.refinery.viatra.runtime.rete.util.Options; +import tools.refinery.viatra.runtime.rete.util.OrderingCompareAgent; + +/** + * @author Gabor Bergmann + * + */ +public class JoinOrderingHeuristics implements Comparator { + + @Override + public int compare(JoinCandidate jc1, JoinCandidate jc2) { + return new OrderingCompareAgent(jc1, jc2) { + @Override + protected void doCompare() { + swallowBoolean(true && consider(preferTrue(a.isTrivial(), b.isTrivial())) + && consider(preferTrue(a.isSubsumption(), b.isSubsumption())) + && consider(preferTrue(a.isCheckOnly(), b.isCheckOnly())) + && consider( + Options.functionalDependencyOption == Options.FunctionalDependencyOption.OFF ? + dontCare() : + preferTrue(a.isHeath(), b.isHeath()) + ) + && consider(preferFalse(a.isDescartes(), b.isDescartes())) + + // TODO main heuristic decisions + + // tie breaking + && consider(preferLess(a.getConsPrimary(), b.getConsPrimary(), TieBreaker.CONSTRAINT_LIST_COMPARATOR)) + && consider(preferLess(a.getConsSecondary(), b.getConsSecondary(), TieBreaker.CONSTRAINT_LIST_COMPARATOR)) + && consider(preferLess(System.identityHashCode(a), System.identityHashCode(b)))); + } + }.compare(); + + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java new file mode 100644 index 00000000..9b814376 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/QuasiTreeLayout.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.construction.quasitree; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +import org.apache.log4j.Logger; +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; +import tools.refinery.viatra.runtime.matchers.planning.helpers.BuildHelper; +import tools.refinery.viatra.runtime.matchers.planning.operations.PApply; +import tools.refinery.viatra.runtime.matchers.planning.operations.PEnumerate; +import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; +import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; +import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException; +import tools.refinery.viatra.runtime.rete.util.ReteHintOptions; + +/** + * Layout ideas: see https://bugs.eclipse.org/bugs/show_bug.cgi?id=398763 + * + * @author Gabor Bergmann + * + */ +public class QuasiTreeLayout implements IQueryPlannerStrategy { + + private IQueryBackendHintProvider hintProvider; + private IQueryBackendContext backendContext; + private QueryAnalyzer queryAnalyzer; + + public QuasiTreeLayout(IQueryBackendContext backendContext) { + this(backendContext, backendContext.getHintProvider()); + } + + public QuasiTreeLayout(IQueryBackendContext backendContext, IQueryBackendHintProvider hintProvider) { + this.backendContext = backendContext; + this.hintProvider = hintProvider; + queryAnalyzer = backendContext.getQueryAnalyzer(); + } + + @Override + public SubPlan plan(PBody pSystem, Logger logger, IQueryMetaContext context) { + return new Scaffold(pSystem, logger, context).run(); + } + + public class Scaffold { + PBody pSystem; + PQuery query; + IQueryMetaContext context; + private QueryEvaluationHint hints; + //IOperationCompiler compiler; + //SubPlanProcessor planProcessor = new SubPlanProcessor(); + SubPlanFactory planFactory; + + Set deferredConstraints = null; + Set enumerableConstraints = null; + Set constantConstraints = null; + Set forefront = new LinkedHashSet(); + Logger logger; + + Scaffold(PBody pSystem, Logger logger, /*IOperationCompiler compiler,*/ IQueryMetaContext context) { + this.pSystem = pSystem; + this.logger = logger; + this.context = context; + this.planFactory = new SubPlanFactory(pSystem); + query = pSystem.getPattern(); + //this.compiler = compiler; + //planProcessor.setCompiler(compiler); + + hints = hintProvider.getQueryEvaluationHint(query); + } + + /** + * @throws ViatraQueryRuntimeException + */ + public SubPlan run() { + try { + logger.debug(String.format( + "%s: patternbody build started for %s", + getClass().getSimpleName(), + query.getFullyQualifiedName())); + + // PROCESS CONSTRAINTS + deferredConstraints = pSystem.getConstraintsOfType(DeferredPConstraint.class); + enumerableConstraints = pSystem.getConstraintsOfType(EnumerablePConstraint.class); + constantConstraints = pSystem.getConstraintsOfType(ConstantValue.class); + + for (EnumerablePConstraint enumerable : enumerableConstraints) { + SubPlan plan = planFactory.createSubPlan(new PEnumerate(enumerable)); + admitSubPlan(plan); + } + if (enumerableConstraints.isEmpty()) { // EXTREME CASE + SubPlan plan = planFactory.createSubPlan(new PStart()); + admitSubPlan(plan); + } + + // JOIN FOREFRONT PLANS WHILE POSSIBLE + while (forefront.size() > 1) { + // TODO QUASI-TREE TRIVIAL JOINS? + + List candidates = generateJoinCandidates(); + JoinOrderingHeuristics ordering = new JoinOrderingHeuristics(); + JoinCandidate selectedJoin = Collections.min(candidates, ordering); + doJoin(selectedJoin); + } + assert (forefront.size() == 1); + + // PROJECT TO PARAMETERS + SubPlan preFinalPlan = forefront.iterator().next(); + SubPlan finalPlan = planFactory.createSubPlan(new PProject(pSystem.getSymbolicParameterVariables()), preFinalPlan); + + // FINAL CHECK, whether all exported variables are present + all constraint satisfied + BuildHelper.finalCheck(pSystem, finalPlan, context); + // TODO integrate the check above in SubPlan / POperation + + logger.debug(String.format( + "%s: patternbody query plan concluded for %s as: %s", + getClass().getSimpleName(), + query.getFullyQualifiedName(), + finalPlan.toLongString())); + return finalPlan; + } catch (RetePatternBuildException ex) { + ex.setPatternDescription(query); + throw ex; + } + } + + public List generateJoinCandidates() { + List candidates = new ArrayList(); + int bIndex = 0; + for (SubPlan b : forefront) { + int aIndex = 0; + for (SubPlan a : forefront) { + if (aIndex++ >= bIndex) + break; + candidates.add(new JoinCandidate(a, b, queryAnalyzer)); + } + bIndex++; + } + return candidates; + } + + private void admitSubPlan(SubPlan plan) { + // are there any unapplied constant filters that we can apply here? + if (ReteHintOptions.prioritizeConstantFiltering.getValueOrDefault(hints)) { + for (ConstantValue constantConstraint : constantConstraints) { + if (!plan.getAllEnforcedConstraints().contains(constantConstraint) && + plan.getVisibleVariables().containsAll(constantConstraint.getAffectedVariables())) { + plan = planFactory.createSubPlan(new PApply(constantConstraint), plan); + } + } + } + // are there any variables that will not be needed anymore and are worth trimming? + // (check only if there are unenforced enumerables, so that there are still upcoming joins) +// if (Options.planTrimOption != Options.PlanTrimOption.OFF && +// !plan.getAllEnforcedConstraints().containsAll(enumerableConstraints)) { + if (true) { + final SubPlan trimmed = BuildHelper.trimUnneccessaryVariables( + planFactory, plan, true, queryAnalyzer); + plan = trimmed; + } + // are there any checkable constraints? + for (DeferredPConstraint deferred : deferredConstraints) { + if (!plan.getAllEnforcedConstraints().contains(deferred)) { + if (deferred.isReadyAt(plan, context)) { + admitSubPlan(planFactory.createSubPlan(new PApply(deferred), plan)); + return; + } + } + } + // if no checkable constraints and no unused variables + forefront.add(plan); + } + + private void doJoin(JoinCandidate selectedJoin) { + forefront.remove(selectedJoin.getPrimary()); + forefront.remove(selectedJoin.getSecondary()); + admitSubPlan(selectedJoin.getJoinedPlan(planFactory)); + } + + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java new file mode 100644 index 00000000..0b955922 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/quasitree/TieBreaker.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.construction.quasitree; + +import java.util.Comparator; + +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.rete.util.LexicographicComparator; + +/** + * Class providing comparators for breaking ties somewhat more deterministically. + * @author Bergmann Gabor + * + */ +public class TieBreaker { + + private TieBreaker() {/*Utility class constructor*/} + + public static final Comparator CONSTRAINT_COMPARATOR = (arg0, arg1) -> arg0.getMonotonousID() - arg1.getMonotonousID(); + + public static final Comparator> CONSTRAINT_LIST_COMPARATOR = + new LexicographicComparator(CONSTRAINT_COMPARATOR); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java new file mode 100644 index 00000000..d32a0449 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/AbstractEvaluatorNode.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.eval; + +import java.util.Iterator; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.single.SingleInputNode; + +/** + * @author Bergmann Gabor + */ +public abstract class AbstractEvaluatorNode extends SingleInputNode implements IEvaluatorNode { + + /** + * @since 1.5 + */ + protected EvaluatorCore core; + + + /** + * @since 1.5 + */ + public AbstractEvaluatorNode(ReteContainer reteContainer, EvaluatorCore core) { + super(reteContainer); + this.core = core; + core.init(this); + } + + /** + * @since 1.5 + */ + @Override + public ReteContainer getReteContainer() { + return getContainer(); + } + + /** + * @since 1.5 + */ + @Override + public String prettyPrintTraceInfoPatternList() { + return getTraceInfoPatternsEnumerated(); + } + + /** + * @since 2.4 + */ + protected void propagateIterableUpdate(final Direction direction, final Iterable update, final Timestamp timestamp) { + final Iterator itr = update.iterator(); + while (itr.hasNext()) { + propagateUpdate(direction, itr.next(), timestamp); + } + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java new file mode 100644 index 00000000..c45c6048 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/EvaluatorCore.java @@ -0,0 +1,180 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.eval; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.apache.log4j.Logger; +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleValueProvider; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.Sets; + +/** + * An instance of this class performs the evaluation of Java expressions. + * + * @author Bergmann Gabor + * @author Tamas Szabo + * @since 1.5 + */ +public abstract class EvaluatorCore { + + protected Logger logger; + protected IExpressionEvaluator evaluator; + /** + * @since 2.4 + */ + protected int sourceTupleWidth; + private Map parameterPositions; + protected IQueryRuntimeContext runtimeContext; + protected IEvaluatorNode evaluatorNode; + + public EvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator, + final Map parameterPositions, final int sourceTupleWidth) { + this.logger = logger; + this.evaluator = evaluator; + this.parameterPositions = parameterPositions; + this.sourceTupleWidth = sourceTupleWidth; + } + + public void init(final IEvaluatorNode evaluatorNode) { + this.evaluatorNode = evaluatorNode; + this.runtimeContext = evaluatorNode.getReteContainer().getNetwork().getEngine().getRuntimeContext(); + } + + /** + * @since 2.4 + */ + public abstract Iterable performEvaluation(final Tuple input); + + protected abstract String evaluationKind(); + + public Object evaluateTerm(final Tuple input) { + // actual evaluation + Object result = null; + try { + final TupleValueProvider tupleParameters = new TupleValueProvider(runtimeContext.unwrapTuple(input), + parameterPositions); + result = evaluator.evaluateExpression(tupleParameters); + } catch (final Exception e) { + logger.warn(String.format( + "The incremental pattern matcher encountered an error during %s evaluation for pattern(s) %s over values %s. Error message: %s. (Developer note: %s in %s)", + evaluationKind(), evaluatorNode.prettyPrintTraceInfoPatternList(), prettyPrintTuple(input), + e.getMessage(), e.getClass().getSimpleName(), this.evaluatorNode), e); + result = errorResult(); + } + + return result; + } + + protected String prettyPrintTuple(final Tuple tuple) { + return tuple.toString(); + } + + protected Object errorResult() { + return null; + } + + public static class PredicateEvaluatorCore extends EvaluatorCore { + + public PredicateEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator, + final Map parameterPositions, final int sourceTupleWidth) { + super(logger, evaluator, parameterPositions, sourceTupleWidth); + } + + @Override + public Iterable performEvaluation(final Tuple input) { + final Object result = evaluateTerm(input); + if (Boolean.TRUE.equals(result)) { + return Collections.singleton(input); + } else { + return null; + } + } + + @Override + protected String evaluationKind() { + return "check()"; + } + + } + + public static class FunctionEvaluatorCore extends EvaluatorCore { + + /** + * @since 2.4 + */ + protected final boolean isUnwinding; + + public FunctionEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator, + final Map parameterPositions, final int sourceTupleWidth) { + this(logger, evaluator, parameterPositions, sourceTupleWidth, false); + } + + /** + * @since 2.4 + */ + public FunctionEvaluatorCore(final Logger logger, final IExpressionEvaluator evaluator, + final Map parameterPositions, final int sourceTupleWidth, final boolean isUnwinding) { + super(logger, evaluator, parameterPositions, sourceTupleWidth); + this.isUnwinding = isUnwinding; + } + + @Override + public Iterable performEvaluation(final Tuple input) { + final Object result = evaluateTerm(input); + if (result != null) { + if (this.isUnwinding) { + final Set resultAsSet = (result instanceof Set) ? (Set) result + : (result instanceof Iterable) ? Sets.newSet((Iterable) result) : null; + + if (resultAsSet != null) { + return () -> { + final Iterator wrapped = resultAsSet.iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return wrapped.hasNext(); + } + + @Override + public Tuple next() { + final Object next = wrapped.next(); + return Tuples.staticArityLeftInheritanceTupleOf(input, + runtimeContext.wrapElement(next)); + } + }; + }; + } else { + throw new IllegalStateException( + "This is an unwinding evaluator, which expects the evaluation result to either be a set or an iterable, but it was " + + result); + } + } else { + return Collections.singleton( + Tuples.staticArityLeftInheritanceTupleOf(input, runtimeContext.wrapElement(result))); + } + } else { + return null; + } + } + + @Override + protected String evaluationKind() { + return "eval" + (this.isUnwinding ? "Unwind" : "") + "()"; + } + + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java new file mode 100644 index 00000000..177433ab --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/IEvaluatorNode.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.eval; + +import tools.refinery.viatra.runtime.rete.network.ReteContainer; + +/** + * This interface is required for the communication between the evaluation core end the evaluator node. + * @author Gabor Bergmann + * @since 1.5 + */ +public interface IEvaluatorNode { + + ReteContainer getReteContainer(); + + String prettyPrintTraceInfoPatternList(); + + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java new file mode 100644 index 00000000..8928645c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/MemorylessEvaluatorNode.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.eval; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * @author Bergmann Gabor + * + */ +public class MemorylessEvaluatorNode extends AbstractEvaluatorNode { + + /** + * @since 1.5 + */ + public MemorylessEvaluatorNode(final ReteContainer reteContainer, final EvaluatorCore core) { + super(reteContainer, core); + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + final Collection parentTuples = new ArrayList(); + propagatePullInto(parentTuples, flush); + for (final Tuple parentTuple : parentTuples) { + final Iterable output = core.performEvaluation(parentTuple); + if (output != null) { + final Iterator itr = output.iterator(); + while (itr.hasNext()) { + collector.add(itr.next()); + } + } + } + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + final Map> parentTuples = CollectionsFactory.createMap(); + propagatePullIntoWithTimestamp(parentTuples, flush); + for (final Entry> entry : parentTuples.entrySet()) { + final Iterable output = core.performEvaluation(entry.getKey()); + if (output != null) { + final Iterator itr = output.iterator(); + while (itr.hasNext()) { + collector.put(itr.next(), entry.getValue()); + } + } + } + } + + @Override + public void update(final Direction direction, final Tuple input, final Timestamp timestamp) { + final Iterable output = core.performEvaluation(input); + if (output != null) { + propagateIterableUpdate(direction, output, timestamp); + } + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java new file mode 100644 index 00000000..40a20c4e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/OutputCachingEvaluatorNode.java @@ -0,0 +1,311 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.eval; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.Clearable; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; + +/** + * An evaluator node that caches the evaluation result. This node is also capable of caching the timestamps associated + * with the result tuples if it is used in recursive differential dataflow evaluation. + * + * @author Bergmann Gabor + * @author Tamas Szabo + */ +public class OutputCachingEvaluatorNode extends AbstractEvaluatorNode implements Clearable, ResumableNode { + + /** + * @since 2.3 + */ + protected NetworkStructureChangeSensitiveLogic logic; + + /** + * @since 2.4 + */ + protected Map> outputCache; + + /** + * Maps input tuples to timestamps. It is wrong to map evaluation result to timestamps because the different input + * tuples may yield the same evaluation result. This field is null as long as this node is in a non-recursive group. + * + * @since 2.4 + */ + protected TimelyMemory memory; + + /** + * @since 2.4 + */ + protected CommunicationGroup group; + + /** + * @since 1.5 + */ + public OutputCachingEvaluatorNode(final ReteContainer reteContainer, final EvaluatorCore core) { + super(reteContainer, core); + reteContainer.registerClearable(this); + this.outputCache = CollectionsFactory.createMap(); + this.logic = createLogic(); + } + + @Override + public CommunicationGroup getCurrentGroup() { + return this.group; + } + + @Override + public void setCurrentGroup(final CommunicationGroup group) { + this.group = group; + } + + @Override + public void networkStructureChanged() { + super.networkStructureChanged(); + this.logic = createLogic(); + } + + @Override + public void clear() { + this.outputCache.clear(); + if (this.memory != null) { + this.memory.clear(); + } + } + + /** + * @since 2.3 + */ + protected NetworkStructureChangeSensitiveLogic createLogic() { + if (this.reteContainer.isTimelyEvaluation() + && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { + if (this.memory == null) { + this.memory = new TimelyMemory(reteContainer.isTimelyEvaluation() && reteContainer + .getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL); + } + return TIMELY; + } else { + return TIMELESS; + } + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + this.logic.pullInto(collector, flush); + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + this.logic.pullIntoWithTimeline(collector, flush); + } + + @Override + public void update(final Direction direction, final Tuple input, final Timestamp timestamp) { + this.logic.update(direction, input, timestamp); + } + + /** + * @since 2.4 + */ + @Override + public Timestamp getResumableTimestamp() { + if (this.memory == null) { + return null; + } else { + return this.memory.getResumableTimestamp(); + } + } + + /** + * @since 2.4 + */ + @Override + public void resumeAt(final Timestamp timestamp) { + this.logic.resumeAt(timestamp); + } + + /** + * @since 2.3 + */ + protected static abstract class NetworkStructureChangeSensitiveLogic { + + /** + * @since 2.4 + */ + public abstract void update(final Direction direction, final Tuple input, final Timestamp timestamp); + + public abstract void pullInto(final Collection collector, final boolean flush); + + /** + * @since 2.4 + */ + public abstract void pullIntoWithTimeline(final Map> collector, final boolean flush); + + /** + * @since 2.4 + */ + public abstract void resumeAt(final Timestamp timestamp); + + } + + private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { + + @Override + public void resumeAt(final Timestamp timestamp) { + // there is nothing to resume in the timeless case because we do not even care about timestamps + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + throw new UnsupportedOperationException(); + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + for (final Iterable output : outputCache.values()) { + if (output != NORESULT) { + final Iterator itr = output.iterator(); + while (itr.hasNext()) { + collector.add(itr.next()); + } + } + } + } + + @Override + public void update(final Direction direction, final Tuple input, final Timestamp timestamp) { + if (direction == Direction.INSERT) { + final Iterable output = core.performEvaluation(input); + if (output != null) { + final Iterable previous = outputCache.put(input, output); + if (previous != null) { + throw new IllegalStateException( + String.format("Duplicate insertion of tuple %s into node %s", input, this)); + } + propagateIterableUpdate(direction, output, timestamp); + } + } else { + final Iterable output = outputCache.remove(input); + if (output != null) { + // may be null if no result was yielded + propagateIterableUpdate(direction, output, timestamp); + } + } + } + }; + + private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { + + @Override + public void resumeAt(final Timestamp timestamp) { + final Map> diffMap = memory.resumeAt(timestamp); + + for (final Entry> entry : diffMap.entrySet()) { + final Tuple input = entry.getKey(); + final Iterable output = outputCache.get(input); + if (output != NORESULT) { + for (final Signed signed : entry.getValue()) { + propagateIterableUpdate(signed.getDirection(), output, signed.getPayload()); + } + } + + if (memory.get(input) == null) { + outputCache.remove(input); + } + } + + final Timestamp nextTimestamp = memory.getResumableTimestamp(); + if (nextTimestamp != null) { + group.notifyHasMessage(mailbox, nextTimestamp); + } + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + for (final Entry> entry : memory.asMap().entrySet()) { + final Tuple input = entry.getKey(); + final Iterable output = outputCache.get(input); + if (output != NORESULT) { + final Timeline timestamp = entry.getValue(); + final Iterator itr = output.iterator(); + while (itr.hasNext()) { + collector.put(itr.next(), timestamp); + } + } + } + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + TIMELESS.pullInto(collector, flush); + } + + @Override + public void update(final Direction direction, final Tuple input, final Timestamp timestamp) { + if (direction == Direction.INSERT) { + Iterable output = outputCache.get(input); + if (output == null) { + output = core.performEvaluation(input); + if (output == null) { + // the evaluation result is really null + output = NORESULT; + } + outputCache.put(input, output); + } + final Diff diff = memory.put(input, timestamp); + if (output != NORESULT) { + for (final Signed signed : diff) { + propagateIterableUpdate(signed.getDirection(), output, signed.getPayload()); + } + } + } else { + final Iterable output = outputCache.get(input); + final Diff diff = memory.remove(input, timestamp); + if (memory.get(input) == null) { + outputCache.remove(input); + } + if (output != NORESULT) { + for (final Signed signed : diff) { + propagateIterableUpdate(signed.getDirection(), output, signed.getPayload()); + } + } + } + } + }; + + /** + * This field is used to represent the "null" evaluation result. This is an optimization used in the timely case + * where the same tuple may be inserted multiple times with different timestamps. This way, we can also cache if + * something evaluated to null (instead of just forgetting about the previously computed result), thus avoiding the + * need to re-run a potentially expensive evaluation. + */ + private static final Iterable NORESULT = Collections + .singleton(Tuples.staticArityFlatTupleOf(NoResult.INSTANCE)); + + private enum NoResult { + INSTANCE + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java new file mode 100644 index 00000000..68d277e8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/eval/RelationEvaluatorNode.java @@ -0,0 +1,183 @@ +/******************************************************************************* + * Copyright (c) 2010-2022, Tamas Szabo, GitHub + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.eval; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.RelationEvaluation; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Clearable; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; +import tools.refinery.viatra.runtime.rete.misc.SimpleReceiver; +import tools.refinery.viatra.runtime.rete.network.ProductionNode; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.StandardNode; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.single.AbstractUniquenessEnforcerNode; + +/** + * A node that operates in batch-style (see {@link Receiver#doesProcessUpdatesInBatch()} and evaluates arbitrary Java + * logic represented by an {@link IRelationEvaluator} on the input relations. This is the backing computation node of a + * {@link RelationEvaluation} constraint. + * + * @author Tamas Szabo + * @since 2.8 + */ +public class RelationEvaluatorNode extends StandardNode implements Supplier, Clearable { + + private final IRelationEvaluator evaluator; + private Set cachedOutputs; + private Supplier[] inputSuppliers; + private BatchingReceiver[] inputReceivers; + + public RelationEvaluatorNode(final ReteContainer container, final IRelationEvaluator evaluator) { + super(container); + this.evaluator = evaluator; + this.reteContainer.registerClearable(this); + } + + @Override + public void clear() { + this.cachedOutputs.clear(); + } + + public void connectToParents(final List inputSuppliers) { + this.inputSuppliers = new Supplier[inputSuppliers.size()]; + this.inputReceivers = new BatchingReceiver[inputSuppliers.size()]; + + final List inputArities = evaluator.getInputArities(); + + if (inputArities.size() != inputSuppliers.size()) { + throw new IllegalStateException(evaluator.toString() + " expects " + inputArities.size() + + " inputs, but got " + inputSuppliers.size() + " input(s)!"); + } + + for (int i = 0; i < inputSuppliers.size(); i++) { + final int currentExpectedInputArity = inputArities.get(i); + final Supplier inputSupplier = inputSuppliers.get(i); + // it is expected that the supplier is a production node because + // the corresponding constraint itself accepts a list of PQuery + if (!(inputSupplier instanceof ProductionNode)) { + throw new IllegalStateException( + evaluator.toString() + " expects each one of its suppliers to be instances of " + + ProductionNode.class.getSimpleName() + " but got an instance of " + + inputSupplier.getClass().getSimpleName() + "!"); + } + final int currentActualInputArity = ((ProductionNode) inputSupplier).getPosMapping().size(); + if (currentActualInputArity != currentExpectedInputArity) { + throw new IllegalStateException( + evaluator.toString() + " expects input arity " + currentExpectedInputArity + " at position " + i + + " but got " + currentActualInputArity + "!"); + } + final BatchingReceiver inputReceiver = new BatchingReceiver((ProductionNode) inputSupplier, + this.reteContainer); + this.inputSuppliers[i] = inputSupplier; + this.inputReceivers[i] = inputReceiver; + this.reteContainer.connectAndSynchronize(inputSupplier, inputReceiver); + reteContainer.getCommunicationTracker().registerDependency(inputReceiver, this); + } + + // initialize the output relation + final List> inputSets = new ArrayList>(); + for (final BatchingReceiver inputReceiver : this.inputReceivers) { + inputSets.add(inputReceiver.getTuples()); + } + this.cachedOutputs = evaluateRelation(inputSets); + } + + @Override + public void networkStructureChanged() { + if (this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { + throw new IllegalStateException(this.toString() + " cannot be used in recursive evaluation!"); + } + super.networkStructureChanged(); + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + collector.addAll(this.cachedOutputs); + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + final Timeline timeline = Timelines.createFrom(Timestamp.ZERO); + for (final Tuple output : this.cachedOutputs) { + collector.put(output, timeline); + } + } + + private Set evaluateRelation(final List> inputs) { + try { + return this.evaluator.evaluateRelation(inputs); + } catch (final Exception e) { + throw new IllegalStateException("Exception during the evaluation of " + this.evaluator.toString() + "!", e); + } + } + + private void batchUpdateCompleted() { + final List> inputSets = new ArrayList>(); + for (final BatchingReceiver inputReceiver : this.inputReceivers) { + inputSets.add(inputReceiver.getTuples()); + } + final Set newOutputs = evaluateRelation(inputSets); + for (final Tuple tuple : newOutputs) { + if (this.cachedOutputs != null && this.cachedOutputs.remove(tuple)) { + // already known tuple - do nothing + } else { + // newly inserted tuple + propagateUpdate(Direction.INSERT, tuple, Timestamp.ZERO); + } + } + if (this.cachedOutputs != null) { + for (final Tuple tuple : this.cachedOutputs) { + // lost tuple + propagateUpdate(Direction.DELETE, tuple, Timestamp.ZERO); + } + } + this.cachedOutputs = newOutputs; + } + + public class BatchingReceiver extends SimpleReceiver { + private final ProductionNode source; + + private BatchingReceiver(final ProductionNode source, final ReteContainer container) { + super(container); + this.source = source; + } + + private Set getTuples() { + return ((AbstractUniquenessEnforcerNode) this.source).getTuples(); + } + + @Override + public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { + throw new UnsupportedOperationException("This receiver only supports batch-style operation!"); + } + + @Override + public void batchUpdate(final Collection> updates, final Timestamp timestamp) { + assert Timestamp.ZERO.equals(timestamp); + // there is nothing to do here because the source production node has already updated itself + // the only thing we need to do is to issue the callback + RelationEvaluatorNode.this.batchUpdateCompleted(); + } + + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java new file mode 100644 index 00000000..6306a482 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DefaultIndexerListener.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.index; + +import java.lang.ref.WeakReference; + +import tools.refinery.viatra.runtime.rete.network.Node; + +public abstract class DefaultIndexerListener implements IndexerListener { + + WeakReference owner; + + public DefaultIndexerListener(Node owner) { + this.owner = new WeakReference(owner); + } + + @Override + public Node getOwner() { + return owner.get(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java new file mode 100644 index 00000000..170ac460 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/DualInputNode.java @@ -0,0 +1,348 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.StandardNode; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp.AllZeroMap; +import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; +import tools.refinery.viatra.runtime.rete.util.Options; + +/** + * Abstract superclass for nodes with two inputs that are matched against each other. + * + * @author Gabor Bergmann + */ +public abstract class DualInputNode extends StandardNode implements NetworkStructureChangeSensitiveNode { + + /** + * @since 2.3 + */ + protected NetworkStructureChangeSensitiveLogic logic; + + public IterableIndexer getPrimarySlot() { + return primarySlot; + } + + public Indexer getSecondarySlot() { + return secondarySlot; + } + + /** + * @author Gabor Bergmann + * + */ + public enum Side { + PRIMARY, SECONDARY, BOTH; + + public Side opposite() { + switch (this) { + case PRIMARY: + return SECONDARY; + case SECONDARY: + return PRIMARY; + case BOTH: + return BOTH; + default: + return BOTH; + } + } + } + + /** + * Holds the primary input slot of this node. + */ + protected IterableIndexer primarySlot; + + /** + * Holds the secondary input slot of this node. + */ + protected Indexer secondarySlot; + + /** + * Optional complementer mask + */ + protected TupleMask complementerSecondaryMask; + + /** + * true if the primary and secondary slots coincide + */ + protected boolean coincidence; + + /** + * @param reteContainer + */ + public DualInputNode(final ReteContainer reteContainer, final TupleMask complementerSecondaryMask) { + super(reteContainer); + this.complementerSecondaryMask = complementerSecondaryMask; + this.indexerGroupCache = CollectionsFactory.createMap(); + this.refreshIndexerGroupCache(); + } + + /** + * Should be called only once, when node is initialized. + */ + public void connectToIndexers(final IterableIndexer primarySlot, final Indexer secondarySlot) { + this.primarySlot = primarySlot; + this.secondarySlot = secondarySlot; + + reteContainer.getCommunicationTracker().registerDependency(primarySlot, this); + reteContainer.getCommunicationTracker().registerDependency(secondarySlot, this); + + // attach listeners + // if there is syncing, do this after the flush done for pulling, but before syncing updates + coincidence = primarySlot.equals(secondarySlot); + + if (!coincidence) { // regular case + primarySlot.attachListener(new DefaultIndexerListener(this) { + @Override + public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement, + final Tuple signature, final boolean change, final Timestamp timestamp) { + DualInputNode.this.logic.notifyUpdate(Side.PRIMARY, direction, updateElement, signature, change, + timestamp); + } + + @Override + public String toString() { + return "primary@" + DualInputNode.this; + } + }); + secondarySlot.attachListener(new DefaultIndexerListener(this) { + public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement, + final Tuple signature, final boolean change, final Timestamp timestamp) { + DualInputNode.this.logic.notifyUpdate(Side.SECONDARY, direction, updateElement, signature, change, + timestamp); + } + + @Override + public String toString() { + return "secondary@" + DualInputNode.this; + } + }); + } else { // if the two slots are the same, updates have to be handled carefully + primarySlot.attachListener(new DefaultIndexerListener(this) { + public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement, + final Tuple signature, final boolean change, final Timestamp timestamp) { + DualInputNode.this.logic.notifyUpdate(Side.BOTH, direction, updateElement, signature, change, + timestamp); + } + + @Override + public String toString() { + return "both@" + DualInputNode.this; + } + }); + } + + for (final Receiver receiver : getReceivers()) { + this.reteContainer.getDelayedCommandQueue() + .add(new DelayedConnectCommand(this, receiver, this.reteContainer)); + } + + // Given that connectToIndexers registers new dependencies, the networkStructureChanged() method will be called + // by the CommunicationTracker, and the implementation of that method in turn will call refreshIndexerGroupCache() anyway. + this.refreshIndexerGroupCache(); + } + + /** + * Helper: retrieves all stored substitutions from the opposite side memory. + * + * @return the collection of opposite substitutions if any, or null if none + */ + protected Collection retrieveOpposites(final Side side, final Tuple signature) { + return getSlot(side.opposite()).get(signature); + } + + /** + * @since 2.3 + */ + protected NetworkStructureChangeSensitiveLogic createLogic() { + if (this.reteContainer.isTimelyEvaluation() + && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { + return createTimelyLogic(); + } else { + return createTimelessLogic(); + } + } + + /** + * Helper: unifies a left and right partial matching. + */ + protected Tuple unify(final Tuple left, final Tuple right) { + return complementerSecondaryMask.combine(left, right, Options.enableInheritance, true); + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + this.logic.pullInto(collector, flush); + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + this.logic.pullIntoWithTimeline(collector, flush); + } + + /** + * Helper: unifies a substitution from the specified side with another substitution from the other side. + */ + protected Tuple unify(final Side side, final Tuple ps, final Tuple opposite) { + switch (side) { + case PRIMARY: + return unify(ps, opposite); + case SECONDARY: + return unify(opposite, ps); + case BOTH: + return unify(ps, opposite); + default: + return null; + } + } + + /** + * Simulates the behavior of the node for calibration purposes only. + */ + public abstract Tuple calibrate(final Tuple primary, final Tuple secondary); + + /** + * @param complementerSecondaryMask + * the complementerSecondaryMask to set + */ + public void setComplementerSecondaryMask(final TupleMask complementerSecondaryMask) { + this.complementerSecondaryMask = complementerSecondaryMask; + } + + /** + * Retrieves the slot corresponding to the specified side. + */ + protected Indexer getSlot(final Side side) { + if (side == Side.SECONDARY) { + return secondarySlot; + } else { + return primarySlot; + } + } + + @Override + public void assignTraceInfo(final TraceInfo traceInfo) { + super.assignTraceInfo(traceInfo); + if (traceInfo.propagateToIndexerParent()) { + if (primarySlot != null) { + primarySlot.acceptPropagatedTraceInfo(traceInfo); + } + if (secondarySlot != null) { + secondarySlot.acceptPropagatedTraceInfo(traceInfo); + } + } + } + + @Override + public void networkStructureChanged() { + super.networkStructureChanged(); + this.logic = createLogic(); + this.refreshIndexerGroupCache(); + } + + /** + * @since 2.3 + */ + protected abstract NetworkStructureChangeSensitiveLogic createTimelyLogic(); + + /** + * @since 2.3 + */ + protected abstract NetworkStructureChangeSensitiveLogic createTimelessLogic(); + + /** + * This map caches the result of a CommunicationTracker.areInSameGroup(indexer, this) call. It does that for both + * the primary and secondary slots. This way we can avoid the lookup in the getWithTimestamp call for each tuple. + * The cache needs to be maintained when the network structure changes. + * @since 2.3 + */ + protected Map indexerGroupCache; + + /** + * @since 2.3 + */ + protected void refreshIndexerGroupCache() { + this.indexerGroupCache.clear(); + if (this.primarySlot != null) { + this.indexerGroupCache.put(this.primarySlot, + this.reteContainer.getCommunicationTracker().areInSameGroup(this.primarySlot, this)); + } + if (this.secondarySlot != null) { + this.indexerGroupCache.put(this.secondarySlot, + this.reteContainer.getCommunicationTracker().areInSameGroup(this.secondarySlot, this)); + } + } + + /** + * @since 2.4 + */ + protected Map> getTimeline(final Tuple signature, final Indexer indexer) { + if (this.indexerGroupCache.get(indexer)) { + // recursive timely case + return indexer.getTimeline(signature); + } else { + // the indexer is in a different group, treat all of its tuples as they would have timestamp 0 + final Collection tuples = indexer.get(signature); + if (tuples == null) { + return null; + } else { + return new AllZeroMap((Set) tuples); + } + } + } + + /** + * @since 2.3 + */ + protected static abstract class NetworkStructureChangeSensitiveLogic { + + /** + * Abstract handler for update event. + * + * @param side + * The side on which the event occurred. + * @param direction + * The direction of the update. + * @param updateElement + * The partial matching that is inserted. + * @param signature + * Masked signature of updateElement. + * @param change + * Indicates whether this is/was the first/last instance of this signature in this slot. + * @since 2.4 + */ + public abstract void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement, + final Tuple signature, final boolean change, final Timestamp timestamp); + + public abstract void pullInto(final Collection collector, final boolean flush); + + /** + * @since 2.4 + */ + public abstract void pullIntoWithTimeline(final Map> collector, final boolean flush); + + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java new file mode 100644 index 00000000..275ff638 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ExistenceNode.java @@ -0,0 +1,199 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * Propagates all substitutions arriving at the PRIMARY slot if and only if (a matching substitution on the SECONDARY is + * present) xor (NEGATIVE). + * + * The negative parameter specifies whether this node checks for existence or non-existence. + *

+ * It is mandatory in differential dataflow evaluation that the secondary parent is in an upstream dependency component + * (so that every secondary tuple comes with zero timestamp). + * + * @author Gabor Bergmann + */ +public class ExistenceNode extends DualInputNode { + + protected boolean negative; + + /** + * @param reteContainer + * @param negative + * if false, act as existence checker, otherwise a nonexistence-checker + */ + public ExistenceNode(final ReteContainer reteContainer, final boolean negative) { + super(reteContainer, null); + this.negative = negative; + this.logic = createLogic(); + } + + @Override + public Tuple calibrate(final Tuple primary, final Tuple secondary) { + return primary; + } + + @Override + public void networkStructureChanged() { + if (this.reteContainer.isTimelyEvaluation() && this.secondarySlot != null + && this.reteContainer.getCommunicationTracker().areInSameGroup(this, this.secondarySlot)) { + throw new IllegalStateException("Secondary parent must be in an upstream dependency component!"); + } + super.networkStructureChanged(); + } + + private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + throw new UnsupportedOperationException(); + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + if (primarySlot == null || secondarySlot == null) { + return; + } + if (flush) { + reteContainer.flushUpdates(); + } + + for (final Tuple signature : primarySlot.getSignatures()) { + // primaries can not be null due to the contract of IterableIndex.getSignatures() + final Collection primaries = primarySlot.get(signature); + final Collection opposites = secondarySlot.get(signature); + if ((opposites != null) ^ negative) { + collector.addAll(primaries); + } + } + } + + @Override + public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement, + final Tuple signature, final boolean change, final Timestamp timestamp) { + // in the default case, all timestamps must be zero + assert Timestamp.ZERO.equals(timestamp); + + switch (side) { + case PRIMARY: + if ((retrieveOpposites(side, signature) != null) ^ negative) { + propagateUpdate(direction, updateElement, timestamp); + } + break; + case SECONDARY: + if (change) { + final Collection opposites = retrieveOpposites(side, signature); + if (opposites != null) { + for (final Tuple opposite : opposites) { + propagateUpdate((negative ? direction.opposite() : direction), opposite, timestamp); + } + } + } + break; + case BOTH: + // in case the slots coincide, + // negative --> always empty + // !positive --> identity + if (!negative) { + propagateUpdate(direction, updateElement, timestamp); + } + break; + } + } + }; + + private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + if (primarySlot == null || secondarySlot == null) { + return; + } + if (flush) { + reteContainer.flushUpdates(); + } + + for (final Tuple signature : primarySlot.getSignatures()) { + // primaries can not be null due to the contract of IterableIndex.getSignatures() + final Map> primaries = getTimeline(signature, primarySlot); + // see contract: secondary must be in an upstream SCC + final Collection opposites = secondarySlot.get(signature); + if ((opposites != null) ^ negative) { + for (final Tuple primary : primaries.keySet()) { + collector.put(primary, primaries.get(primary)); + } + } + } + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + ExistenceNode.this.TIMELESS.pullInto(collector, flush); + } + + @Override + public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement, + final Tuple signature, final boolean change, final Timestamp timestamp) { + switch (side) { + case PRIMARY: { + final Collection opposites = secondarySlot.get(signature); + if ((opposites != null) ^ negative) { + propagateUpdate(direction, updateElement, timestamp); + } + break; + } + case SECONDARY: { + final Map> opposites = primarySlot.getTimeline(signature); + if (change) { + if (opposites != null) { + for (final Tuple opposite : opposites.keySet()) { + for (final Signed oppositeSigned : opposites.get(opposite).asChangeSequence()) { + final Direction product = direction.multiply(oppositeSigned.getDirection()); + propagateUpdate((negative ? product.opposite() : product), opposite, + oppositeSigned.getPayload()); + } + } + } + } + break; + } + case BOTH: + // in case the slots coincide, + // negative --> always empty + // positive --> identity + if (!negative) { + propagateUpdate(direction, updateElement, timestamp); + } + break; + } + } + }; + + @Override + protected NetworkStructureChangeSensitiveLogic createTimelessLogic() { + return this.TIMELESS; + } + + @Override + protected NetworkStructureChangeSensitiveLogic createTimelyLogic() { + return this.TIMELY; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java new file mode 100644 index 00000000..3de10def --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/GenericProjectionIndexer.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * A generic Indexer capable of indexing along any valid TupleMask. Does not keep track of parents, because will not + * ever pull parents. + * + * @author Gabor Bergmann + * + */ +public class GenericProjectionIndexer extends IndexerWithMemory implements ProjectionIndexer { + + public GenericProjectionIndexer(ReteContainer reteContainer, TupleMask mask) { + super(reteContainer, mask); + } + + @Override + protected void update(Direction direction, Tuple updateElement, Tuple signature, boolean change, + Timestamp timestamp) { + propagate(direction, updateElement, signature, change, timestamp); + } + + @Override + public Collection get(Tuple signature) { + return memory.get(signature); + } + + @Override + public Map> getTimeline(Tuple signature) { + return memory.getWithTimeline(signature); + } + + @Override + public Iterator iterator() { + return memory.iterator(); + } + + @Override + public Iterable getSignatures() { + return memory.getSignatures(); + } + + /** + * @since 2.0 + */ + @Override + public int getBucketCount() { + return memory.getKeysetSize(); + } + + @Override + public Receiver getActiveNode() { + return this; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java new file mode 100644 index 00000000..6c158f2c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IdentityIndexer.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2004-2012 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * Defines an abstract trivial indexer that identically projects the contents of some stateful node, and can therefore + * save space. Can only exist in connection with a stateful store, and must be operated by another node (the active + * node). Do not attach parents directly! + * + * @author Gabor Bergmann + * @noimplement Rely on the provided implementations + * @noreference Use only via standard Node and Indexer interfaces + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public abstract class IdentityIndexer extends SpecializedProjectionIndexer { + + protected abstract Collection getTuples(); + + public IdentityIndexer(ReteContainer reteContainer, int tupleWidth, Supplier parent, + Node activeNode, List sharedSubscriptionList) { + super(reteContainer, TupleMask.identity(tupleWidth), parent, activeNode, sharedSubscriptionList); + } + + @Override + public Collection get(Tuple signature) { + if (contains(signature)) { + return Collections.singleton(signature); + } else + return null; + } + + protected boolean contains(Tuple signature) { + return getTuples().contains(signature); + } + + @Override + public Collection getSignatures() { + return getTuples(); + } + + @Override + public int getBucketCount() { + return getTuples().size(); + } + + @Override + public Iterator iterator() { + return getTuples().iterator(); + } + + @Override + public void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement, Timestamp timestamp) { + listener.notifyIndexerUpdate(direction, updateElement, updateElement, true, timestamp); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java new file mode 100644 index 00000000..fc9d7781 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/Indexer.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * A node that indexes incoming Tuples by their signatures as specified by a TupleMask. Notifies listeners about such + * update events through the IndexerListener. + * + * Signature tuples are created by transforming the update tuples using the mask. Tuples stored with the same signature + * are grouped together. The group or a reduction thereof is retrievable. + * + * @author Gabor Bergmann + */ +public interface Indexer extends Node { + /** + * @return the mask by which the contents are indexed. + */ + public TupleMask getMask(); + + /** + * @return the node whose contents are indexed. + */ + public Supplier getParent(); + + /** + * @return all stored tuples that conform to the specified signature, null if there are none such. CONTRACT: do not + * modify! + */ + public Collection get(Tuple signature); + + /** + * @since 2.4 + */ + default public Map> getTimeline(Tuple signature) { + throw new UnsupportedOperationException(); + } + + /** + * This indexer will be updated whenever a Rete update is sent to the active node (or an equivalent time slot + * allotted to it). The active node is typically the indexer itself, but it can be a different node such as its + * parent. + * + * @return the active node that operates this indexer + */ + public Node getActiveNode(); + + + public Collection getListeners(); + + public void attachListener(IndexerListener listener); + + public void detachListener(IndexerListener listener); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java new file mode 100644 index 00000000..f52b6a06 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerListener.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * A listener for update events concerning an Indexer. + * + * @author Gabor Bergmann + * + */ +public interface IndexerListener { + /** + * Notifies recipient that the indexer has just received an update. Contract: indexer already reflects the updated + * state. + * + * @param direction + * the direction of the update. + * @param updateElement + * the tuple that was updated. + * @param signature + * the signature of the tuple according to the indexer's mask. + * @param change + * whether this was the first inserted / last revoked update element with this particular signature. + * @since 2.4 + */ + void notifyIndexerUpdate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp); + + Node getOwner(); +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java new file mode 100644 index 00000000..a31562e9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IndexerWithMemory.java @@ -0,0 +1,284 @@ +/******************************************************************************* + * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; +import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; + +/** + * @author Gabor Bergmann + * @author Tamas Szabo + */ +public abstract class IndexerWithMemory extends StandardIndexer + implements Receiver, NetworkStructureChangeSensitiveNode, ResumableNode { + + protected MaskedTupleMemory memory; + + /** + * @since 2.3 + */ + protected NetworkStructureChangeSensitiveLogic logic; + + /** + * @since 1.6 + */ + protected final Mailbox mailbox; + + /** + * @since 2.4 + */ + protected CommunicationGroup group; + + public IndexerWithMemory(final ReteContainer reteContainer, final TupleMask mask) { + super(reteContainer, mask); + final boolean isTimely = reteContainer.isTimelyEvaluation() + && reteContainer.getCommunicationTracker().isInRecursiveGroup(this); + memory = MaskedTupleMemory.create(mask, MemoryType.SETS, this, isTimely, isTimely && reteContainer + .getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL); + reteContainer.registerClearable(memory); + mailbox = instantiateMailbox(); + reteContainer.registerClearable(mailbox); + this.logic = createLogic(); + } + + @Override + public CommunicationGroup getCurrentGroup() { + return this.group; + } + + @Override + public void setCurrentGroup(final CommunicationGroup group) { + this.group = group; + } + + @Override + public void networkStructureChanged() { + super.networkStructureChanged(); + final boolean wasTimely = this.memory.isTimely(); + final boolean isTimely = this.reteContainer.isTimelyEvaluation() + && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this); + if (wasTimely != isTimely) { + final MaskedTupleMemory newMemory = MaskedTupleMemory.create(mask, MemoryType.SETS, this, + isTimely, isTimely && reteContainer.getTimelyConfiguration() + .getTimelineRepresentation() == TimelineRepresentation.FAITHFUL); + newMemory.initializeWith(this.memory, Timestamp.ZERO); + memory.clear(); + memory = newMemory; + } + this.logic = createLogic(); + } + + /** + * Instantiates the {@link Mailbox} of this receiver. Subclasses may override this method to provide their own + * mailbox implementation. + * + * @return the mailbox + * @since 2.0 + */ + protected Mailbox instantiateMailbox() { + if (this.reteContainer.isTimelyEvaluation()) { + return new TimelyMailbox(this, this.reteContainer); + } else { + return new BehaviorChangingMailbox(this, this.reteContainer); + } + } + + @Override + public Mailbox getMailbox() { + return this.mailbox; + } + + /** + * @since 2.0 + */ + public MaskedTupleMemory getMemory() { + return memory; + } + + @Override + public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { + this.logic.update(direction, updateElement, timestamp); + } + + /** + * Refined version of update + * + * @since 2.4 + */ + protected abstract void update(final Direction direction, final Tuple updateElement, final Tuple signature, + final boolean change, final Timestamp timestamp); + + @Override + public void appendParent(final Supplier supplier) { + if (parent == null) { + parent = supplier; + } else { + throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent + + ") and cannot connect to additional parent (" + supplier + "). "); + } + } + + @Override + public void removeParent(final Supplier supplier) { + if (parent == supplier) { + parent = null; + } else { + throw new IllegalArgumentException( + "Illegal RETE edge removal: the parent of " + this + " is not " + supplier); + } + } + + /** + * @since 2.4 + */ + @Override + public Collection getParents() { + return Collections.singleton(parent); + } + + /** + * @since 2.4 + */ + @Override + public void resumeAt(final Timestamp timestamp) { + this.logic.resumeAt(timestamp); + } + + /** + * @since 2.4 + */ + @Override + public Timestamp getResumableTimestamp() { + return this.memory.getResumableTimestamp(); + } + + /** + * @since 2.3 + */ + protected static abstract class NetworkStructureChangeSensitiveLogic { + + /** + * @since 2.4 + */ + public abstract void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp); + + /** + * @since 2.4 + */ + public abstract void resumeAt(final Timestamp timestamp); + + } + + /** + * @since 2.3 + */ + protected NetworkStructureChangeSensitiveLogic createLogic() { + if (this.reteContainer.isTimelyEvaluation() + && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { + return TIMELY; + } else { + return TIMELESS; + } + } + + private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { + + @Override + public void resumeAt(final Timestamp timestamp) { + final Iterable signatures = memory.getResumableSignatures(); + + final Map wasPresent = CollectionsFactory.createMap(); + for (final Tuple signature : signatures) { + wasPresent.put(signature, memory.isPresentAtInfinity(signature)); + } + + final Map>> signatureMap = memory.resumeAt(timestamp); + + for (final Entry>> outerEntry : signatureMap.entrySet()) { + final Tuple signature = outerEntry.getKey(); + final Map> diffMap = outerEntry.getValue(); + final boolean isPresent = memory.isPresentAtInfinity(signature); + // only send out a potential true value the first time for a given signature, then set it to false + boolean change = wasPresent.get(signature) ^ isPresent; + + for (final Entry> innerEntry : diffMap.entrySet()) { + final Tuple tuple = innerEntry.getKey(); + final Diff diffs = innerEntry.getValue(); + for (final Signed signed : diffs) { + IndexerWithMemory.this.update(signed.getDirection(), tuple, signature, change, + signed.getPayload()); + } + // change is a signature-wise flag, so it is ok to "try" to signal it for the first tuple only + change = false; + } + } + + final Timestamp nextTimestamp = memory.getResumableTimestamp(); + if (nextTimestamp != null) { + group.notifyHasMessage(mailbox, nextTimestamp); + } + } + + @Override + public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { + final Tuple signature = mask.transform(update); + final boolean wasPresent = memory.isPresentAtInfinity(signature); + final Diff resultDiff = direction == Direction.INSERT + ? memory.addWithTimestamp(update, signature, timestamp) + : memory.removeWithTimestamp(update, signature, timestamp); + final boolean isPresent = memory.isPresentAtInfinity(signature); + final boolean change = wasPresent ^ isPresent; + for (final Signed signed : resultDiff) { + IndexerWithMemory.this.update(signed.getDirection(), update, signature, change, signed.getPayload()); + } + } + + }; + + private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { + + @Override + public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { + final Tuple signature = mask.transform(update); + final boolean change = direction == Direction.INSERT ? memory.add(update, signature) + : memory.remove(update, signature); + IndexerWithMemory.this.update(direction, update, signature, change, timestamp); + } + + @Override + public void resumeAt(final Timestamp timestamp) { + // there is nothing to resume in the timeless case because we do not even care about timestamps + } + + }; + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java new file mode 100644 index 00000000..d6f8ef05 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/IterableIndexer.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * An indexer that allows the iteration of all retrievable tuple groups (or reduced groups). + * + * @author Gabor Bergmann + * + */ +public interface IterableIndexer extends Indexer, Iterable { + + /** + * A view consisting of exactly those signatures whose tuple group is not empty + * @since 2.0 + */ + public Iterable getSignatures(); + + /** + * @return the number of signatures whose tuple group is not empty + * @since 2.0 + */ + public int getBucketCount(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java new file mode 100644 index 00000000..9a6a0de9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/JoinNode.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * @author Gabor Bergmann + * + */ +public class JoinNode extends DualInputNode { + + public JoinNode(final ReteContainer reteContainer, final TupleMask complementerSecondaryMask) { + super(reteContainer, complementerSecondaryMask); + this.logic = createLogic(); + } + + @Override + public Tuple calibrate(final Tuple primary, final Tuple secondary) { + return unify(primary, secondary); + } + + private final NetworkStructureChangeSensitiveLogic TIMELESS = new NetworkStructureChangeSensitiveLogic() { + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + throw new UnsupportedOperationException(); + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + if (primarySlot == null || secondarySlot == null) { + return; + } + + if (flush) { + reteContainer.flushUpdates(); + } + + for (final Tuple signature : primarySlot.getSignatures()) { + // primaries can not be null due to the contract of IterableIndex.getSignatures() + final Collection primaries = primarySlot.get(signature); + final Collection opposites = secondarySlot.get(signature); + if (opposites != null) { + for (final Tuple primary : primaries) { + for (final Tuple opposite : opposites) { + collector.add(unify(primary, opposite)); + } + } + } + } + } + + @Override + public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement, + final Tuple signature, final boolean change, final Timestamp timestamp) { + // in the default case, all timestamps must be zero + assert Timestamp.ZERO.equals(timestamp); + + final Collection opposites = retrieveOpposites(side, signature); + + if (!coincidence) { + if (opposites != null) { + for (final Tuple opposite : opposites) { + propagateUpdate(direction, unify(side, updateElement, opposite), timestamp); + } + } + } else { + // compensate for coincidence of slots - this is the case when an Indexer is joined with itself + if (opposites != null) { + for (final Tuple opposite : opposites) { + if (opposite.equals(updateElement)) { + // handle self-joins of a single tuple separately + continue; + } + propagateUpdate(direction, unify(opposite, updateElement), timestamp); + propagateUpdate(direction, unify(updateElement, opposite), timestamp); + } + } + + // handle self-joins here + propagateUpdate(direction, unify(updateElement, updateElement), timestamp); + } + } + }; + + private final NetworkStructureChangeSensitiveLogic TIMELY = new NetworkStructureChangeSensitiveLogic() { + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + if (primarySlot == null || secondarySlot == null) { + return; + } + + if (flush) { + reteContainer.flushUpdates(); + } + + for (final Tuple signature : primarySlot.getSignatures()) { + // primaries can not be null due to the contract of IterableIndex.getSignatures() + final Map> primaries = getTimeline(signature, primarySlot); + final Map> opposites = getTimeline(signature, secondarySlot); + if (opposites != null) { + for (final Tuple primary : primaries.keySet()) { + for (final Tuple opposite : opposites.keySet()) { + final Timeline primaryTimeline = primaries.get(primary); + final Timeline oppositeTimeline = opposites.get(opposite); + final Timeline mergedTimeline = primaryTimeline + .mergeMultiplicative(oppositeTimeline); + if (!mergedTimeline.isEmpty()) { + collector.put(unify(primary, opposite), mergedTimeline); + } + } + } + } + } + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + JoinNode.this.TIMELESS.pullInto(collector, flush); + } + + @Override + public void notifyUpdate(final Side side, final Direction direction, final Tuple updateElement, + final Tuple signature, final boolean change, final Timestamp timestamp) { + final Indexer oppositeIndexer = getSlot(side.opposite()); + final Map> opposites = getTimeline(signature, oppositeIndexer); + + if (!coincidence) { + if (opposites != null) { + for (final Tuple opposite : opposites.keySet()) { + final Tuple unifiedTuple = unify(side, updateElement, opposite); + for (final Signed signed : opposites.get(opposite).asChangeSequence()) { + // TODO only consider signed timestamps that are greater or equal to timestamp + // plus compact the previous timestamps into at most one update + propagateUpdate(signed.getDirection().multiply(direction), unifiedTuple, + timestamp.max(signed.getPayload())); + } + } + } + } else { + // compensate for coincidence of slots - this is the case when an Indexer is joined with itself + if (opposites != null) { + for (final Tuple opposite : opposites.keySet()) { + if (opposite.equals(updateElement)) { + // handle self-joins of a single tuple separately + continue; + } + final Tuple u1 = unify(opposite, updateElement); + final Tuple u2 = unify(updateElement, opposite); + for (final Signed oppositeSigned : opposites.get(opposite).asChangeSequence()) { + final Direction updateDirection = direction.multiply(oppositeSigned.getDirection()); + final Timestamp updateTimestamp = timestamp.max(oppositeSigned.getPayload()); + propagateUpdate(updateDirection, u1, updateTimestamp); + propagateUpdate(updateDirection, u2, updateTimestamp); + } + } + } + + // handle self-join here + propagateUpdate(direction, unify(updateElement, updateElement), timestamp); + } + } + }; + + @Override + protected NetworkStructureChangeSensitiveLogic createTimelessLogic() { + return this.TIMELESS; + } + + @Override + protected NetworkStructureChangeSensitiveLogic createTimelyLogic() { + return this.TIMELY; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java new file mode 100644 index 00000000..59b75c33 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryIdentityIndexer.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.List; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; + +/** + * Defines a trivial indexer that identically projects the contents of a memory-equipped node, and can therefore save + * space. Can only exist in connection with a memory, and must be operated by another node. Do not attach parents + * directly! + * + * @noimplement Rely on the provided implementations + * @noreference Use only via standard Node and Indexer interfaces + * @noinstantiate This class is not intended to be instantiated by clients. + * @author Gabor Bergmann + */ + +public class MemoryIdentityIndexer extends IdentityIndexer { + + protected final Collection memory; + + /** + * @param reteContainer + * @param tupleWidth + * the width of the tuples of memoryNode + * @param memory + * the memory whose contents are to be identity-indexed + * @param parent + * the parent node that owns the memory + */ + public MemoryIdentityIndexer(ReteContainer reteContainer, int tupleWidth, Collection memory, + Supplier parent, Receiver activeNode, List sharedSubscriptionList) { + super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList); + this.memory = memory; + } + + @Override + protected Collection getTuples() { + return this.memory; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java new file mode 100644 index 00000000..204fe433 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/MemoryNullIndexer.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.List; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; + +/** + * Defines a trivial indexer that projects the contents of a memory-equipped node to the empty tuple, and can therefore + * save space. Can only exist in connection with a memory, and must be operated by another node. Do not attach parents + * directly! + * + * @author Gabor Bergmann + * @noimplement Rely on the provided implementations + * @noreference Use only via standard Node and Indexer interfaces + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public class MemoryNullIndexer extends NullIndexer { + + Collection memory; + + /** + * @param reteContainer + * @param tupleWidth + * the width of the tuples of memoryNode + * @param memory + * the memory whose contents are to be null-indexed + * @param parent + * the parent node that owns the memory + */ + public MemoryNullIndexer(ReteContainer reteContainer, int tupleWidth, Collection memory, + Supplier parent, Receiver activeNode, List sharedSubscriptionList) { + super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList); + this.memory = memory; + } + + @Override + protected Collection getTuples() { + return this.memory; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java new file mode 100644 index 00000000..a875b29f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/NullIndexer.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2004-2012 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * Defines an abstract trivial indexer that projects the contents of some stateful node to the empty tuple, and can + * therefore save space. Can only exist in connection with a stateful store, and must be operated by another node (the + * active node). Do not attach parents directly! + * + * @author Gabor Bergmann + * @noimplement Rely on the provided implementations + * @noreference Use only via standard Node and Indexer interfaces + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public abstract class NullIndexer extends SpecializedProjectionIndexer { + + protected abstract Collection getTuples(); + + protected static final Tuple nullSignature = Tuples.staticArityFlatTupleOf(); + protected static final Collection nullSingleton = Collections.singleton(nullSignature); + protected static final Collection emptySet = Collections.emptySet(); + + public NullIndexer(ReteContainer reteContainer, int tupleWidth, Supplier parent, Node activeNode, + List sharedSubscriptionList) { + super(reteContainer, TupleMask.linear(0, tupleWidth), parent, activeNode, sharedSubscriptionList); + } + + @Override + public Collection get(Tuple signature) { + if (nullSignature.equals(signature)) + return isEmpty() ? null : getTuples(); + else + return null; + } + + @Override + public Collection getSignatures() { + return isEmpty() ? emptySet : nullSingleton; + } + + protected boolean isEmpty() { + return getTuples().isEmpty(); + } + + protected boolean isSingleElement() { + return getTuples().size() == 1; + } + + @Override + public Iterator iterator() { + return getTuples().iterator(); + } + + @Override + public int getBucketCount() { + return getTuples().isEmpty() ? 0 : 1; + } + + @Override + public void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement, + Timestamp timestamp) { + boolean radical = (direction == Direction.DELETE && isEmpty()) + || (direction == Direction.INSERT && isSingleElement()); + listener.notifyIndexerUpdate(direction, updateElement, nullSignature, radical, timestamp); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java new file mode 100644 index 00000000..fef84bb1 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/OnetimeIndexer.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; + +/** + * @author Gabor Bergmann Indexer whose lifetime last until the first get() DO NOT connect to nodes! + */ +public class OnetimeIndexer extends GenericProjectionIndexer { + + public OnetimeIndexer(ReteContainer reteContainer, TupleMask mask) { + super(reteContainer, mask); + } + + @Override + public Collection get(Tuple signature) { + if (tools.refinery.viatra.runtime.rete.util.Options.releaseOnetimeIndexers) { + reteContainer.unregisterClearable(memory); + reteContainer.unregisterNode(this); + } + return super.get(signature); + } + + @Override + public void appendParent(Supplier supplier) { + throw new UnsupportedOperationException("onetime indexer cannot have parents"); + } + + @Override + public void attachListener(IndexerListener listener) { + throw new UnsupportedOperationException("onetime indexer cannot have listeners"); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java new file mode 100644 index 00000000..58e593d9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/ProjectionIndexer.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2004-2009 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +/** + * An iterable indexer that receives updates from a node, and groups received tuples intact, i.e. it does not reduce + * tuple groups. + * + * @author Gabor Bergmann + * + */ +public interface ProjectionIndexer extends IterableIndexer { + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java new file mode 100644 index 00000000..9c647aa9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/SpecializedProjectionIndexer.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2004-2012 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * A specialized projection indexer that can be memory-less (relying on an external source of information). + * + *

+ * All specialized projection indexers of a single node will share the same listener list, so that notification order is + * maintained (see Bug 518434). + * + * @author Gabor Bergmann + * @noimplement Rely on the provided implementations + * @noreference Use only via standard Node and Indexer interfaces + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public abstract class SpecializedProjectionIndexer extends StandardIndexer implements ProjectionIndexer { + + protected Node activeNode; + protected List subscriptions; + + /** + * @since 1.7 + */ + public SpecializedProjectionIndexer(final ReteContainer reteContainer, final TupleMask mask, final Supplier parent, + final Node activeNode, final List subscriptions) { + super(reteContainer, mask); + this.parent = parent; + this.activeNode = activeNode; + this.subscriptions = subscriptions; + } + + public List getSubscriptions() { + return subscriptions; + } + + @Override + public Node getActiveNode() { + return activeNode; + } + + @Override + protected void propagate(final Direction direction, final Tuple updateElement, final Tuple signature, + final boolean change, final Timestamp timestamp) { + throw new UnsupportedOperationException(); + } + + @Override + public void attachListener(final IndexerListener listener) { + super.attachListener(listener); + final CommunicationTracker tracker = this.getCommunicationTracker(); + final IndexerListener proxy = tracker.proxifyIndexerListener(this, listener); + final ListenerSubscription subscription = new ListenerSubscription(this, proxy); + tracker.registerDependency(this, proxy.getOwner()); + // See Bug 518434 + // Must add to the first position, so that the later listeners are notified earlier. + // Thus if the beta node added as listener is also an indirect descendant of the same indexer on its opposite + // slot, + // then the beta node is connected later than its ancestor's listener, therefore it will be notified earlier, + // eliminating duplicate insertions and lost deletions that would result from fall-through update propagation + subscriptions.add(0, subscription); + } + + @Override + public void detachListener(final IndexerListener listener) { + final CommunicationTracker tracker = this.getCommunicationTracker(); + // obtain the proxy before the super call would unregister the dependency + final IndexerListener proxy = tracker.proxifyIndexerListener(this, listener); + super.detachListener(listener); + final ListenerSubscription subscription = new ListenerSubscription(this, proxy); + final boolean wasContained = subscriptions.remove(subscription); + assert wasContained; + tracker.unregisterDependency(this, proxy.getOwner()); + } + + @Override + public void networkStructureChanged() { + super.networkStructureChanged(); + final List oldSubscriptions = new ArrayList(); + oldSubscriptions.addAll(subscriptions); + subscriptions.clear(); + for (final ListenerSubscription oldSubscription : oldSubscriptions) { + // there is no need to unregister and re-register the dependency between indexer and listener + // because the owner of the listener is the same (even if it is proxified) + final CommunicationTracker tracker = this.getCommunicationTracker(); + // the subscriptions are shared, so we MUST reuse the indexer of the subscription instead of simply 'this' + final IndexerListener proxy = tracker.proxifyIndexerListener(oldSubscription.indexer, oldSubscription.listener); + final ListenerSubscription newSubscription = new ListenerSubscription(oldSubscription.indexer, proxy); + subscriptions.add(newSubscription); + } + } + + /** + * @since 2.4 + */ + public abstract void propagateToListener(IndexerListener listener, Direction direction, Tuple updateElement, + Timestamp timestamp); + + /** + * Infrastructure to share subscriptions between specialized indexers of the same parent node. + * + * @author Gabor Bergmann + * @since 1.7 + */ + public static class ListenerSubscription { + protected SpecializedProjectionIndexer indexer; + protected IndexerListener listener; + + public ListenerSubscription(SpecializedProjectionIndexer indexer, IndexerListener listener) { + super(); + this.indexer = indexer; + this.listener = listener; + } + + /** + * @since 2.4 + */ + public SpecializedProjectionIndexer getIndexer() { + return indexer; + } + + /** + * @since 2.4 + */ + public IndexerListener getListener() { + return listener; + } + + /** + * Call this from parent node. + * @since 2.4 + */ + public void propagate(Direction direction, Tuple updateElement, Timestamp timestamp) { + indexer.propagateToListener(listener, direction, updateElement, timestamp); + } + + @Override + public int hashCode() { + return Objects.hash(indexer, listener); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ListenerSubscription other = (ListenerSubscription) obj; + return Objects.equals(listener, other.listener) && Objects.equals(indexer, other.indexer); + } + + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java new file mode 100644 index 00000000..9847a8dd --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/StandardIndexer.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.List; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.BaseNode; +import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; + +/** + * An abstract standard implementation of the Indexer interface, providing common bookkeeping functionality. + * + * @author Gabor Bergmann + * + */ +public abstract class StandardIndexer extends BaseNode implements Indexer, NetworkStructureChangeSensitiveNode { + + protected Supplier parent; + private final List originalListeners; + private final List proxyListeners; + protected TupleMask mask; + + public StandardIndexer(ReteContainer reteContainer, TupleMask mask) { + super(reteContainer); + this.parent = null; + this.mask = mask; + this.originalListeners = CollectionsFactory.createObserverList(); + this.proxyListeners = CollectionsFactory.createObserverList(); + } + + /** + * @since 2.4 + */ + protected void propagate(Direction direction, Tuple updateElement, Tuple signature, boolean change, Timestamp timestamp) { + for (IndexerListener listener : proxyListeners) { + listener.notifyIndexerUpdate(direction, updateElement, signature, change, timestamp); + } + } + + @Override + public TupleMask getMask() { + return mask; + } + + @Override + public Supplier getParent() { + return parent; + } + + @Override + public void attachListener(IndexerListener listener) { + this.getCommunicationTracker().registerDependency(this, listener.getOwner()); + // obtain the proxy after registering the dependency because then the proxy reflects the new SCC structure + final IndexerListener proxy = this.getCommunicationTracker().proxifyIndexerListener(this, listener); + // See Bug 518434 + // Must add to the first position, so that the later listeners are notified earlier. + // Thus if the beta node added as listener is also an indirect descendant of the same indexer on its opposite slot, + // then the beta node is connected later than its ancestor's listener, therefore it will be notified earlier, + // eliminating duplicate insertions and lost deletions that would result from fall-through update propagation + this.originalListeners.add(0, listener); + this.proxyListeners.add(0, proxy); + } + + @Override + public void detachListener(IndexerListener listener) { + this.originalListeners.remove(listener); + IndexerListener listenerToRemove = null; + for (final IndexerListener proxyListener : this.proxyListeners) { + if (proxyListener.getOwner() == listener.getOwner()) { + listenerToRemove = proxyListener; + break; + } + } + assert listenerToRemove != null; + this.proxyListeners.remove(listenerToRemove); + this.getCommunicationTracker().unregisterDependency(this, listener.getOwner()); + } + + @Override + public void networkStructureChanged() { + this.proxyListeners.clear(); + for (final IndexerListener original : this.originalListeners) { + this.proxyListeners.add(this.getCommunicationTracker().proxifyIndexerListener(this, original)); + } + } + + @Override + public Collection getListeners() { + return proxyListeners; + } + + @Override + public ReteContainer getContainer() { + return reteContainer; + } + + @Override + protected String toStringCore() { + return super.toStringCore() + "(" + parent + "/" + mask + ")"; + } + + @Override + public void assignTraceInfo(TraceInfo traceInfo) { + super.assignTraceInfo(traceInfo); + if (traceInfo.propagateFromIndexerToSupplierParent()) + if (parent != null) + parent.acceptPropagatedTraceInfo(traceInfo); + } + + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java new file mode 100644 index 00000000..b0bea8cb --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.index; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.alg.incscc.IncSCCAlg; +import tools.refinery.viatra.runtime.matchers.tuple.MaskedTuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.single.TransitiveClosureNode; + +// UNFINISHED, not used yet +public class TransitiveClosureNodeIndexer extends StandardIndexer implements IterableIndexer { + private TransitiveClosureNode tcNode; + private IncSCCAlg tcAlg; + private Collection emptySet; + + public TransitiveClosureNodeIndexer(TupleMask mask, IncSCCAlg tcAlg, TransitiveClosureNode tcNode) { + super(tcNode.getContainer(), mask); + this.tcAlg = tcAlg; + this.tcNode = tcNode; + this.emptySet = Collections.emptySet(); + this.parent = tcNode; + } + + @Override + public Collection get(Tuple signature) { + if (signature.getSize() == mask.sourceWidth) { + if (mask.indices.length == 0) { + // mask ()/2 + return getSignatures(); + } else if (mask.indices.length == 1) { + Set retSet = CollectionsFactory.createSet(); + + // mask (0)/2 + if (mask.indices[0] == 0) { + Object source = signature.get(0); + for (Object target : tcAlg.getAllReachableTargets(source)) { + retSet.add(Tuples.staticArityFlatTupleOf(source, target)); + } + return retSet; + } + // mask (1)/2 + if (mask.indices[0] == 1) { + Object target = signature.get(1); + for (Object source : tcAlg.getAllReachableSources(target)) { + retSet.add(Tuples.staticArityFlatTupleOf(source, target)); + } + return retSet; + } + } else { + // mask (0,1)/2 + if (mask.indices[0] == 0 && mask.indices[1] == 1) { + Object source = signature.get(0); + Object target = signature.get(1); + Tuple singleton = Tuples.staticArityFlatTupleOf(source, target); + return (tcAlg.isReachable(source, target) ? Collections.singleton(singleton) : emptySet); + } + // mask (1,0)/2 + if (mask.indices[0] == 1 && mask.indices[1] == 0) { + Object source = signature.get(1); + Object target = signature.get(0); + Tuple singleton = Tuples.staticArityFlatTupleOf(source, target); + return (tcAlg.isReachable(source, target) ? Collections.singleton(singleton) : emptySet); + } + } + } + return null; + } + + @Override + public int getBucketCount() { + throw new UnsupportedOperationException(); + } + + @Override + public Collection getSignatures() { + return asTupleCollection(tcAlg.getTcRelation()); + } + + @Override + public Iterator iterator() { + return asTupleCollection(tcAlg.getTcRelation()).iterator(); + } + + private Collection asTupleCollection( + Collection> tuples) { + Set retSet = CollectionsFactory.createSet(); + for (tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple tuple : tuples) { + retSet.add(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget())); + } + return retSet; + } + + /** + * @since 2.4 + */ + public void propagate(Direction direction, Tuple updateElement, boolean change) { + propagate(direction, updateElement, new MaskedTuple(updateElement, mask), change, null); + } + + @Override + public Receiver getActiveNode() { + return tcNode; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java new file mode 100644 index 00000000..4319ee29 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryIdentityIndexer.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.index.timely; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.index.IdentityIndexer; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +public class TimelyMemoryIdentityIndexer extends IdentityIndexer { + + protected final TimelyMemory memory; + + public TimelyMemoryIdentityIndexer(final ReteContainer reteContainer, final int tupleWidth, + final TimelyMemory memory, final Supplier parent, final Receiver activeNode, + final List sharedSubscriptionList) { + super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList); + this.memory = memory; + } + + @Override + public Map> getTimeline(final Tuple signature) { + final Timeline timestamp = this.memory.get(signature); + if (timestamp != null) { + return Collections.singletonMap(signature, timestamp); + } else { + return null; + } + } + + @Override + protected Collection getTuples() { + return this.memory.getTuplesAtInfinity(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java new file mode 100644 index 00000000..0386b006 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/timely/TimelyMemoryNullIndexer.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.index.timely; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.index.NullIndexer; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +public class TimelyMemoryNullIndexer extends NullIndexer { + + protected final TimelyMemory memory; + + public TimelyMemoryNullIndexer(final ReteContainer reteContainer, final int tupleWidth, + final TimelyMemory memory, final Supplier parent, + final Receiver activeNode, final List sharedSubscriptionList) { + super(reteContainer, tupleWidth, parent, activeNode, sharedSubscriptionList); + this.memory = memory; + } + + @Override + public Map> getTimeline(final Tuple signature) { + if (nullSignature.equals(signature)) { + return isEmpty() ? null : this.memory.asMap(); + } else { + return null; + } + } + + @Override + protected Collection getTuples() { + return this.memory.getTuplesAtInfinity(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java new file mode 100644 index 00000000..83e86fe6 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/DRedReteBackendFactory.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.matcher; + +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; + +/** + * A {@link ReteBackendFactory} implementation that creates {@link ReteEngine}s that use delete and re-derive + * evaluation. + * + * @author Tamas Szabo + * @since 2.2 + */ +public class DRedReteBackendFactory extends ReteBackendFactory { + + public static final DRedReteBackendFactory INSTANCE = new DRedReteBackendFactory(); + + @Override + public IQueryBackend create(IQueryBackendContext context) { + return create(context, true, null); + } + + @Override + public int hashCode() { + return DRedReteBackendFactory.class.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof DRedReteBackendFactory)) { + return false; + } + return true; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java new file mode 100644 index 00000000..a4fa4914 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/HintConfigurator.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.matcher; + +import java.util.HashMap; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * A configurable hint provider that gathers hints for queries during runtime, and delegates defaults to an external hint provider. + * + * @author Gabor Bergmann + * @since 1.5 + */ +class HintConfigurator implements IQueryBackendHintProvider { + + private IQueryBackendHintProvider defaultHintProvider; + private Map storedHints = new HashMap(); + + public HintConfigurator(IQueryBackendHintProvider defaultHintProvider) { + this.defaultHintProvider = defaultHintProvider; + } + + @Override + public QueryEvaluationHint getQueryEvaluationHint(PQuery query) { + return defaultHintProvider.getQueryEvaluationHint(query).overrideBy(storedHints.get(query)); + } + + public void storeHint(PQuery query, QueryEvaluationHint hint) { + QueryEvaluationHint oldHint = storedHints.get(query); + if (oldHint == null) + storedHints.put(query, hint); + else + storedHints.put(query, oldHint.overrideBy(hint)); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java new file mode 100644 index 00000000..4c64a1a1 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/IncrementalMatcherCapability.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.matcher; + +import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; + +/** + * @author Grill Balázs + * @since 1.4 + * + */ +public class IncrementalMatcherCapability implements IMatcherCapability { + + @Override + public boolean canBeSubstitute(IMatcherCapability capability) { + /* + * TODO: for now, as we are only prepared for Rete and LS, we can assume that + * a matcher created with Rete can always be a substitute for a matcher created + * by any backend. + */ + return true; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java new file mode 100644 index 00000000..347cabc4 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactory.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.matcher; + +import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.rete.construction.plancompiler.ReteRecipeCompiler; +import tools.refinery.viatra.runtime.rete.util.Options; + +public class ReteBackendFactory implements IQueryBackendFactory { + /** + * EXPERIMENTAL + */ + protected static final int reteThreads = 0; + + /** + * @since 2.0 + */ + public static final ReteBackendFactory INSTANCE = new ReteBackendFactory(); + + /** + * @deprecated Use the static {@link #INSTANCE} field instead + */ + @Deprecated + public ReteBackendFactory() { + } + + /** + * @since 1.5 + */ + @Override + public IQueryBackend create(IQueryBackendContext context) { + return create(context, false, null); + } + + /** + * @since 2.4 + */ + public IQueryBackend create(IQueryBackendContext context, boolean deleteAndRederiveEvaluation, + TimelyConfiguration timelyConfiguration) { + ReteEngine engine; + engine = new ReteEngine(context, reteThreads, deleteAndRederiveEvaluation, timelyConfiguration); + IQueryBackendHintProvider hintConfiguration = engine.getHintConfiguration(); + ReteRecipeCompiler compiler = new ReteRecipeCompiler( + Options.builderMethod.layoutStrategy(context, hintConfiguration), context.getLogger(), + context.getRuntimeContext().getMetaContext(), context.getQueryCacheContext(), hintConfiguration, + context.getQueryAnalyzer(), deleteAndRederiveEvaluation, timelyConfiguration); + engine.setCompiler(compiler); + return engine; + } + + @Override + public Class getBackendClass() { + return ReteEngine.class; + } + + @Override + public int hashCode() { + return ReteBackendFactory.class.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof ReteBackendFactory)) { + return false; + } + return true; + } + + /** + * @since 1.4 + */ + @Override + public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint) { + return new IncrementalMatcherCapability(); + } + + @Override + public boolean isCaching() { + return true; + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java new file mode 100644 index 00000000..98775ab3 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteBackendFactoryProvider.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.matcher; + +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactoryProvider; + +/** + * @since 2.0 + * + */ +public class ReteBackendFactoryProvider implements IQueryBackendFactoryProvider { + + @Override + public IQueryBackendFactory getFactory() { + return ReteBackendFactory.INSTANCE; + } + + @Override + public boolean isSystemDefaultEngine() { + return true; + } + + @Override + public boolean isSystemDefaultCachingBackend() { + return true; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java new file mode 100644 index 00000000..9bd499f4 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/ReteEngine.java @@ -0,0 +1,579 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.matcher; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collection; +import java.util.LinkedList; +import java.util.Map; +import java.util.concurrent.Callable; + +import org.apache.log4j.Logger; +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; +import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.rete.boundary.Disconnectable; +import tools.refinery.viatra.runtime.rete.boundary.ReteBoundary; +import tools.refinery.viatra.runtime.rete.construction.RetePatternBuildException; +import tools.refinery.viatra.runtime.rete.construction.plancompiler.ReteRecipeCompiler; +import tools.refinery.viatra.runtime.rete.index.Indexer; +import tools.refinery.viatra.runtime.rete.network.Network; +import tools.refinery.viatra.runtime.rete.network.NodeProvisioner; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; + +/** + * @author Gabor Bergmann + * + */ +public class ReteEngine implements IQueryBackend { + + protected Network reteNet; + protected final int reteThreads; + protected ReteBoundary boundary; + + /** + * @since 2.2 + */ + protected final boolean deleteAndRederiveEvaluation; + /** + * @since 2.4 + */ + protected final TimelyConfiguration timelyConfiguration; + + private IQueryBackendContext context; + private Logger logger; + protected IQueryRuntimeContext runtimeContext; + + protected Collection disconnectables; + + protected Map matchers; + + protected ReteRecipeCompiler compiler; + + protected final boolean parallelExecutionEnabled; // TRUE if model manipulation can go on + + private boolean disposedOrUninitialized = true; + + private HintConfigurator hintConfigurator; + + /** + * @param context + * the context of the pattern matcher, conveying all information from the outside world. + * @param reteThreads + * the number of threads to operate the RETE network with; 0 means single-threaded operation, 1 starts an + * asynchronous thread to operate the RETE net, >1 uses multiple RETE containers. + */ + public ReteEngine(IQueryBackendContext context, int reteThreads) { + this(context, reteThreads, false, null); + } + + /** + * @since 2.4 + */ + public ReteEngine(IQueryBackendContext context, int reteThreads, boolean deleteAndRederiveEvaluation, TimelyConfiguration timelyConfiguration) { + super(); + this.context = context; + this.logger = context.getLogger(); + this.runtimeContext = context.getRuntimeContext(); + this.reteThreads = reteThreads; + this.parallelExecutionEnabled = reteThreads > 0; + this.deleteAndRederiveEvaluation = deleteAndRederiveEvaluation; + this.timelyConfiguration = timelyConfiguration; + initEngine(); + this.compiler = null; + } + + /** + * @since 1.6 + */ + public IQueryBackendContext getBackendContext() { + return context; + } + + /** + * @since 2.2 + */ + public boolean isDeleteAndRederiveEvaluation() { + return this.deleteAndRederiveEvaluation; + } + + /** + * @since 2.4 + */ + public TimelyConfiguration getTimelyConfiguration() { + return this.timelyConfiguration; + } + + /** + * initializes engine components + */ + private synchronized void initEngine() { + this.disposedOrUninitialized = false; + this.disconnectables = new LinkedList(); + // this.caughtExceptions = new LinkedBlockingQueue(); + + + this.hintConfigurator = new HintConfigurator(context.getHintProvider()); + + this.reteNet = new Network(reteThreads, this); + this.boundary = new ReteBoundary(this); // prerequisite: network + + this.matchers = CollectionsFactory.createMap(); + /* this.matchersScoped = new HashMap,RetePatternMatcher>>(); */ + + // prerequisite: network, framework, boundary, disconnectables + //context.subscribeBackendForUpdates(this.boundary); + // prerequisite: boundary, disconnectables +// this.traceListener = context.subscribePatternMatcherForTraceInfluences(this); + + } + + @Override + public void flushUpdates() { + for (ReteContainer container : this.reteNet.getContainers()) { + container.deliverMessagesSingleThreaded(); + } + } + + /** + * deconstructs engine components + */ + private synchronized void deconstructEngine() { + ensureInitialized(); + reteNet.kill(); + + //context.unSubscribeBackendFromUpdates(this.boundary); + for (Disconnectable disc : disconnectables) { + disc.disconnect(); + } + + this.matchers = null; + this.disconnectables = null; + + this.reteNet = null; + this.boundary = null; + + this.hintConfigurator = null; + + // this.machineListener = new MachineListener(this); // prerequisite: + // framework, disconnectables +// this.traceListener = null; + + this.disposedOrUninitialized = true; + } + + /** + * Deconstructs the engine to get rid of it finally + */ + public void killEngine() { + deconstructEngine(); + // this.framework = null; + this.compiler = null; + this.logger = null; + } + + /** + * Resets the engine to an after-initialization phase + * + */ + public void reset() { + deconstructEngine(); + + initEngine(); + + compiler.reset(); + } + + /** + * Accesses the patternmatcher for a given pattern, constructs one if a matcher is not available yet. + * + * @pre: builder is set. + * @param query + * the pattern to be matched. + * @return a patternmatcher object that can match occurences of the given pattern. + * @throws ViatraQueryRuntimeException + * if construction fails. + */ + public synchronized RetePatternMatcher accessMatcher(final PQuery query) { + ensureInitialized(); + RetePatternMatcher matcher; + // String namespace = gtPattern.getNamespace().getName(); + // String name = gtPattern.getName(); + // String fqn = namespace + "." + name; + matcher = matchers.get(query); + if (matcher == null) { + constructionWrapper(() -> { + RecipeTraceInfo prodNode; + prodNode = boundary.accessProductionTrace(query); + + RetePatternMatcher retePatternMatcher = new RetePatternMatcher(ReteEngine.this, + prodNode); + retePatternMatcher.setTag(query); + matchers.put(query, retePatternMatcher); + return null; + }); + matcher = matchers.get(query); + } + + executeDelayedCommands(); + + return matcher; + } + + + /** + * Constructs RETE pattern matchers for a collection of patterns, if they are not available yet. Model traversal + * during the whole construction period is coalesced (which may have an effect on performance, depending on the + * matcher context). + * + * @pre: builder is set. + * @param specifications + * the patterns to be matched. + * @throws ViatraQueryRuntimeException + * if construction fails. + */ + public synchronized void buildMatchersCoalesced(final Collection specifications) { + ensureInitialized(); + constructionWrapper(() -> { + for (PQuery specification : specifications) { + boundary.accessProductionNode(specification); + } + return null; + }); + } + + /** + * @since 2.4 + */ + public T constructionWrapper(final Callable payload) { + T result = null; +// context.modelReadLock(); +// try { + if (parallelExecutionEnabled) + reteNet.getStructuralChangeLock().lock(); + try { + try { + result = runtimeContext.coalesceTraversals(() -> { + T innerResult = payload.call(); + this.executeDelayedCommands(); + return innerResult; + }); + } catch (InvocationTargetException ex) { + final Throwable cause = ex.getCause(); + if (cause instanceof RetePatternBuildException) + throw (RetePatternBuildException) cause; + if (cause instanceof RuntimeException) + throw (RuntimeException) cause; + assert (false); + } + } finally { + if (parallelExecutionEnabled) + reteNet.getStructuralChangeLock().unlock(); + reteNet.waitForReteTermination(); + } +// } finally { +// context.modelReadUnLock(); +// } + return result; + } + + // /** + // * Accesses the patternmatcher for a given pattern with additional scoping, constructs one if + // * a matcher is not available yet. + // * + // * @param gtPattern + // * the pattern to be matched. + // * @param additionalScopeMap + // * additional, optional scopes for the symbolic parameters + // * maps the position of the symbolic parameter to its additional scope (if any) + // * @pre: scope.parent is non-root, i.e. this is a nontrivial constraint + // * use the static method RetePatternMatcher.buildAdditionalScopeMap() to create from PatternCallSignature + // * @return a patternmatcher object that can match occurences of the given + // * pattern. + // * @throws PatternMatcherCompileTimeException + // * if construction fails. + // */ + // public synchronized RetePatternMatcher accessMatcherScoped(PatternDescription gtPattern, Map + // additionalScopeMap) + // throws PatternMatcherCompileTimeException { + // if (additionalScopeMap.isEmpty()) return accessMatcher(gtPattern); + // + // RetePatternMatcher matcher; + // + // Map, RetePatternMatcher> scopes = matchersScoped.get(gtPattern); + // if (scopes == null) { + // scopes = new HashMap, RetePatternMatcher>(); + // matchersScoped.put(gtPattern, scopes); + // } + // + // matcher = scopes.get(additionalScopeMap); + // if (matcher == null) { + // context.modelReadLock(); + // try { + // reteNet.getStructuralChangeLock().lock(); + // try { + // Address prodNode; + // prodNode = boundary.accessProductionScoped(gtPattern, additionalScopeMap); + // + // matcher = new RetePatternMatcher(this, prodNode); + // scopes.put(additionalScopeMap, matcher); + // } finally { + // reteNet.getStructuralChangeLock().unlock(); + // } + // } finally { + // context.modelReadUnLock(); + // } + // // reteNet.flushUpdates(); + // } + // + // return matcher; + // } + + /** + * Returns an indexer that groups the contents of this Production node by their projections to a given mask. + * Designed to be called by a RetePatternMatcher. + * + * @param production + * the production node to be indexed. + * @param mask + * the mask that defines the projection. + * @return the Indexer. + */ + synchronized Indexer accessProjection(RecipeTraceInfo production, TupleMask mask) { + ensureInitialized(); + NodeProvisioner nodeProvisioner = reteNet.getHeadContainer().getProvisioner(); + Indexer result = nodeProvisioner.peekProjectionIndexer(production, mask); + if (result == null) { + result = constructionWrapper(() -> + nodeProvisioner.accessProjectionIndexerOnetime(production, mask) + ); + } + + return result; + } + + // /** + // * Retrieves the patternmatcher for a given pattern fqn, returns null if + // the matching network hasn't been constructed yet. + // * + // * @param fqn the fully qualified name of the pattern to be matched. + // * @return the previously constructed patternmatcher object that can match + // occurences of the given pattern, or null if it doesn't exist. + // */ + // public RetePatternMatcher getMatcher(String fqn) + // { + // RetePatternMatcher matcher = matchersByFqn.get(fqn); + // if (matcher == null) + // { + // Production prodNode = boundary.getProduction(fqn); + // + // matcher = new RetePatternMatcher(this, prodNode); + // matchersByFqn.put(fqn, matcher); + // } + // + // return matcher; + // } + + /** + * @since 2.3 + */ + public void executeDelayedCommands() { + for (final ReteContainer container : this.reteNet.getContainers()) { + container.executeDelayedCommands(); + } + } + + /** + * Waits until the pattern matcher is in a steady state and output can be retrieved. + */ + public void settle() { + ensureInitialized(); + reteNet.waitForReteTermination(); + } + + /** + * Waits until the pattern matcher is in a steady state and output can be retrieved. When steady state is reached, a + * retrieval action is executed before the steady state ceases. + * + * @param action + * the action to be run when reaching the steady-state. + */ + public void settle(Runnable action) { + ensureInitialized(); + reteNet.waitForReteTermination(action); + } + + // /** + // * @return the framework + // */ + // public IFramework getFramework() { + // return framework.get(); + // } + + /** + * @return the reteNet + */ + public Network getReteNet() { + ensureInitialized(); + return reteNet; + } + + /** + * @return the boundary + */ + public ReteBoundary getBoundary() { + ensureInitialized(); + return boundary; + } + + // /** + // * @return the pattern matcher builder + // */ + // public IRetePatternBuilder getBuilder() { + // return builder; + // } + + /** + * @param builder + * the pattern matcher builder to set + */ + public void setCompiler(ReteRecipeCompiler builder) { + ensureInitialized(); + this.compiler = builder; + } + +// /** +// * @return the manipulationListener +// */ +// public IManipulationListener getManipulationListener() { +// ensureInitialized(); +// return manipulationListener; +// } + +// /** +// * @return the traceListener +// */ +// public IPredicateTraceListener geTraceListener() { +// ensureInitialized(); +// return traceListener; +// } + + /** + * @param disc + * the new Disconnectable adapter. + */ + public void addDisconnectable(Disconnectable disc) { + ensureInitialized(); + disconnectables.add(disc); + } + + /** + * @return the parallelExecutionEnabled + */ + public boolean isParallelExecutionEnabled() { + return parallelExecutionEnabled; + } + + + public Logger getLogger() { + ensureInitialized(); + return logger; + } + + public IQueryRuntimeContext getRuntimeContext() { + ensureInitialized(); + return runtimeContext; + } + + public ReteRecipeCompiler getCompiler() { + ensureInitialized(); + return compiler; + } + + // /** + // * For internal use only: logs exceptions occurring during term evaluation inside the RETE net. + // * @param e + // */ + // public void logEvaluatorException(Throwable e) { + // try { + // caughtExceptions.put(e); + // } catch (InterruptedException e1) { + // logEvaluatorException(e); + // } + // } + // /** + // * Polls the exceptions caught and logged during term evaluation by this RETE engine. + // * Recommended usage: iterate polling until null is returned. + // * + // * @return the next caught exception, or null if there are no more. + // */ + // public Throwable getNextLoggedEvaluatorException() { + // return caughtExceptions.poll(); + // } + + void ensureInitialized() { + if (disposedOrUninitialized) + throw new IllegalStateException("Trying to use a Rete engine that has been disposed or has not yet been initialized."); + + } + + @Override + public IQueryResultProvider getResultProvider(PQuery query) { + return accessMatcher(query); + } + + /** + * @since 1.4 + */ + @Override + public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints) { + hintConfigurator.storeHint(query, hints); + return accessMatcher(query); + } + + @Override + public IQueryResultProvider peekExistingResultProvider(PQuery query) { + ensureInitialized(); + return matchers.get(query); + } + + @Override + public void dispose() { + killEngine(); + } + + @Override + public boolean isCaching() { + return true; + } + + /** + * @since 1.5 + * @noreference Internal API, subject to change + */ + public IQueryBackendHintProvider getHintConfiguration() { + return hintConfigurator; + } + + @Override + public IQueryBackendFactory getFactory() { + return ReteBackendFactory.INSTANCE; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java new file mode 100644 index 00000000..1f380b45 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java @@ -0,0 +1,462 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.matcher; + +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; +import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; +import tools.refinery.viatra.runtime.matchers.backend.IUpdateable; +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.Accuracy; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.rete.index.Indexer; +import tools.refinery.viatra.runtime.rete.index.IterableIndexer; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.ProductionNode; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.remote.Address; +import tools.refinery.viatra.runtime.rete.single.CallbackNode; +import tools.refinery.viatra.runtime.rete.single.TransformerNode; +import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * @author Gabor Bergmann + * + */ +public class RetePatternMatcher extends TransformerNode implements IQueryResultProvider { + + protected ReteEngine engine; + protected IQueryRuntimeContext context; + protected ProductionNode productionNode; + protected RecipeTraceInfo productionNodeTrace; + protected Map posMapping; + protected Map taggedChildren = CollectionsFactory.createMap(); + protected boolean connected = false; // is rete-wise connected to the + // production node? + + /** + * @param productionNode + * a production node that matches this pattern without any parameter bindings + * @pre: Production must be local to the head container + */ + public RetePatternMatcher(ReteEngine engine, RecipeTraceInfo productionNodeTrace) { + super(engine.getReteNet().getHeadContainer()); + this.engine = engine; + this.context = engine.getRuntimeContext(); + this.productionNodeTrace = productionNodeTrace; + final Address productionAddress = reteContainer.getProvisioner() + .getOrCreateNodeByRecipe(productionNodeTrace); + if (!reteContainer.isLocal(productionAddress)) + throw new IllegalArgumentException("@pre: Production must be local to the head container"); + this.productionNode = (ProductionNode) reteContainer.resolveLocal(productionAddress); + this.posMapping = this.productionNode.getPosMapping(); + this.reteContainer.getCommunicationTracker().registerDependency(this.productionNode, this); + } + + /** + * @since 1.6 + */ + public ProductionNode getProductionNode() { + return productionNode; + } + + public Tuple matchOneRandomly(Object[] inputMapping, boolean[] fixed) { + List allMatches = matchAll(inputMapping, fixed).collect(Collectors.toList()); + if (allMatches == null || allMatches.isEmpty()) + return null; + else + return allMatches.get((int) (Math.random() * allMatches.size())); + } + + /** + * @since 2.0 + */ + public Stream matchAll(Object[] inputMapping, boolean[] fixed) { + // retrieving the projection + TupleMask mask = TupleMask.fromKeepIndicators(fixed); + Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping)); + + return matchAll(mask, inputSignature); + + } + + /** + * @since 2.0 + */ + public Stream matchAll(TupleMask mask, ITuple inputSignature) { + AllMatchFetcher fetcher = new AllMatchFetcher(engine.accessProjection(productionNodeTrace, mask), + context.wrapTuple(inputSignature.toImmutable())); + engine.reteNet.waitForReteTermination(fetcher); + return fetcher.getMatches(); + + } + + /** + * @since 2.0 + */ + public Optional matchOne(Object[] inputMapping, boolean[] fixed) { + // retrieving the projection + TupleMask mask = TupleMask.fromKeepIndicators(fixed); + Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping)); + + return matchOne(mask, inputSignature); + } + + /** + * @since 2.0 + */ + public Optional matchOne(TupleMask mask, ITuple inputSignature) { + SingleMatchFetcher fetcher = new SingleMatchFetcher(engine.accessProjection(productionNodeTrace, mask), + context.wrapTuple(inputSignature.toImmutable())); + engine.reteNet.waitForReteTermination(fetcher); + return Optional.ofNullable(fetcher.getMatch()); + } + + /** + * Counts the number of occurrences of the pattern that match inputMapping on positions where fixed is true. + * + * @return the number of occurrences + */ + public int count(Object[] inputMapping, boolean[] fixed) { + TupleMask mask = TupleMask.fromKeepIndicators(fixed); + Tuple inputSignature = mask.transform(Tuples.flatTupleOf(inputMapping)); + + return count(mask, inputSignature); + } + + /** + * Counts the number of occurrences of the pattern that match inputMapping on positions where fixed is true. + * + * @return the number of occurrences + * @since 1.7 + */ + public int count(TupleMask mask, ITuple inputSignature) { + CountFetcher fetcher = new CountFetcher(engine.accessProjection(productionNodeTrace, mask), + context.wrapTuple(inputSignature.toImmutable())); + engine.reteNet.waitForReteTermination(fetcher); + + return fetcher.getCount(); + } + + /** + * Counts the number of distinct tuples attainable from the match set by projecting match tuples according to the given mask. + * + * + * @return the size of the projection + * @since 2.1 + */ + public int projectionSize(TupleMask groupMask) { + ProjectionSizeFetcher fetcher = new ProjectionSizeFetcher( + (IterableIndexer) engine.accessProjection(productionNodeTrace, groupMask)); + engine.reteNet.waitForReteTermination(fetcher); + + return fetcher.getSize(); + } + + /** + * Connects a new external receiver that will receive update notifications from now on. The receiver will + * practically connect to the production node, the added value is unwrapping the updates for external use. + * + * @param synchronize + * if true, the contents of the production node will be inserted into the receiver after the connection + * is established. + */ + public synchronized void connect(Receiver receiver, boolean synchronize) { + if (!connected) { // connect to the production node as a RETE-child + reteContainer.connect(productionNode, this); + connected = true; + } + if (synchronize) + reteContainer.connectAndSynchronize(this, receiver); + else + reteContainer.connect(this, receiver); + } + + /** + * Connects a new external receiver that will receive update notifications from now on. The receiver will + * practically connect to the production node, the added value is unwrapping the updates for external use. + * + * The external receiver will be disconnectable later based on its tag. + * + * @param tag + * an identifier to recognize the child node by. + * + * @param synchronize + * if true, the contents of the production node will be inserted into the receiver after the connection + * is established. + * + */ + public synchronized void connect(Receiver receiver, Object tag, boolean synchronize) { + taggedChildren.put(tag, receiver); + connect(receiver, synchronize); + } + + /** + * Disconnects a child node. + */ + public synchronized void disconnect(Receiver receiver) { + reteContainer.disconnect(this, receiver); + } + + /** + * Disconnects the child node that was connected by specifying the given tag. + * + * @return if a child node was found registered with this tag. + */ + public synchronized boolean disconnectByTag(Object tag) { + final Receiver receiver = taggedChildren.remove(tag); + final boolean found = receiver != null; + if (found) + disconnect(receiver); + return found; + } + + @Override + protected Tuple transform(Tuple input) { + return context.unwrapTuple(input); + } + + abstract class AbstractMatchFetcher implements Runnable { + Indexer indexer; + Tuple signature; + + public AbstractMatchFetcher(Indexer indexer, Tuple signature) { + super(); + this.indexer = indexer; + this.signature = signature; + } + + @Override + public void run() { + fetch(indexer.get(signature)); + } + + protected abstract void fetch(Collection matches); + + } + + class AllMatchFetcher extends AbstractMatchFetcher { + + public AllMatchFetcher(Indexer indexer, Tuple signature) { + super(indexer, signature); + } + + Stream matches = null; + + public Stream getMatches() { + return matches; + } + + @Override + protected void fetch(Collection matches) { + if (matches == null) + this.matches = Stream.of(); + else { + this.matches = matches.stream().map(context::unwrapTuple); + } + + } + + } + + class SingleMatchFetcher extends AbstractMatchFetcher { + + public SingleMatchFetcher(Indexer indexer, Tuple signature) { + super(indexer, signature); + } + + Tuple match = null; + + public Tuple getMatch() { + return match; + } + + @Override + protected void fetch(Collection matches) { + if (matches != null && !matches.isEmpty()) + match = context.unwrapTuple(matches.iterator().next()); + } + + // public void run() { + // Collection unscopedMatches = indexer.get(signature); + // + // // checking scopes + // if (unscopedMatches != null) { + // for (Tuple um : /* productionNode */unscopedMatches) { + // match = inputConnector.unwrapTuple(um); + // return; + // + // // Tuple ps = inputConnector.unwrapTuple(um); + // // boolean ok = true; + // // if (!ignoreScope) for (int k = 0; (k < ps.getSize()) && ok; k++) { + // // if (pcs[k].getParameterMode() == ParameterMode.INPUT) { + // // // ok = ok && (inputMapping[k]==ps.elements[k]); + // // // should now be true + // // } else // ParameterMode.OUTPUT + // // { + // // IEntity scopeParent = (IEntity) pcs[k].getParameterScope().getParent(); + // // Integer containmentMode = pcs[k].getParameterScope().getContainmentMode(); + // // if (containmentMode == Scope.BELOW) + // // ok = ok && ((IModelElement) ps.get(k)).isBelowNamespace(scopeParent); + // // else + // // /* case Scope.IN: */ + // // ok = ok && scopeParent.equals(((IModelElement) ps.get(k)).getNamespace()); + // // // note: getNamespace returns null instead of the + // // // (imaginary) modelspace root entity for top level + // // // elements; + // // // this is not a problem here as Scope.IN implies + // // // scopeParent != root. + // // + // // } + // // } + // // + // // if (ok) { + // // reteMatching = new ReteMatching(ps, posMapping); + // // return; + // // } + // } + // } + // + // } + + } + + class CountFetcher extends AbstractMatchFetcher { + + public CountFetcher(Indexer indexer, Tuple signature) { + super(indexer, signature); + } + + int count = 0; + + public int getCount() { + return count; + } + + @Override + protected void fetch(Collection matches) { + count = matches == null ? 0 : matches.size(); + } + + } + + class ProjectionSizeFetcher implements Runnable { + IterableIndexer indexer; + int size = 0; + + public ProjectionSizeFetcher(IterableIndexer indexer) { + super(); + this.indexer = indexer; + } + + @Override + public void run() { + size = indexer.getBucketCount(); + } + + public int getSize() { + return size; + } + + } + + private boolean[] notNull(Object[] parameters) { + boolean[] notNull = new boolean[parameters.length]; + for (int i = 0; i < parameters.length; ++i) + notNull[i] = parameters[i] != null; + return notNull; + } + + + + @Override + public boolean hasMatch(Object[] parameters) { + return countMatches(parameters) > 0; + } + + @Override + public boolean hasMatch(TupleMask parameterSeedMask, ITuple parameters) { + return count(parameterSeedMask, parameters) > 0; + } + + @Override + public int countMatches(Object[] parameters) { + return count(parameters, notNull(parameters)); + } + + @Override + public int countMatches(TupleMask parameterSeedMask, ITuple parameters) { + return count(parameterSeedMask, parameters); + } + + + @Override + public Optional estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy) { + return Optional.of((long)projectionSize(groupMask)); // always accurate + } + + @Override + public Optional getOneArbitraryMatch(Object[] parameters) { + return matchOne(parameters, notNull(parameters)); + } + + @Override + public Optional getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters) { + return matchOne(parameterSeedMask, parameters); + } + + @Override + public Stream getAllMatches(Object[] parameters) { + return matchAll(parameters, notNull(parameters)); + } + + @Override + public Stream getAllMatches(TupleMask parameterSeedMask, ITuple parameters) { + return matchAll(parameterSeedMask, parameters); + } + + @Override + public IQueryBackend getQueryBackend() { + return engine; + } + + @Override + public void addUpdateListener(final IUpdateable listener, final Object listenerTag, boolean fireNow) { + // As a listener is added as a delayed command, they should be executed to make sure everything is consistent on + // return, see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=562369 + engine.constructionWrapper(() -> { + final CallbackNode callbackNode = new CallbackNode(this.reteContainer, listener); + connect(callbackNode, listenerTag, fireNow); + return null; + }); + } + + @Override + public void removeUpdateListener(Object listenerTag) { + engine.constructionWrapper(() -> { + disconnectByTag(listenerTag); + return null; + }); + } + + public Indexer getInternalIndexer(TupleMask mask) { + return engine.accessProjection(productionNodeTrace, mask); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java new file mode 100644 index 00000000..876ddc99 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyConfiguration.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.matcher; + +/** + * Configuration of timely evaluation. + * + * @author Tamas Szabo + * @since 2.4 + */ +public class TimelyConfiguration { + + private final AggregatorArchitecture aggregatorArchitecture; + private final TimelineRepresentation timelineRepresentation; + + public TimelyConfiguration(final TimelineRepresentation timelineRepresentation, + final AggregatorArchitecture aggregatorArchitecture) { + this.aggregatorArchitecture = aggregatorArchitecture; + this.timelineRepresentation = timelineRepresentation; + } + + public AggregatorArchitecture getAggregatorArchitecture() { + return aggregatorArchitecture; + } + + public TimelineRepresentation getTimelineRepresentation() { + return timelineRepresentation; + } + + public enum AggregatorArchitecture { + /** + * Aggregands are copied over from lower timestamps to higher timestamps. + */ + PARALLEL, + + /** + * Aggregands are only present at the timestamp where they are inserted at. + * Only aggregate results are pushed towards higher timestamps during folding. + */ + SEQUENTIAL + } + + public enum TimelineRepresentation { + /** + * Only the first moment (timestamp) of appearance is maintained per tuple. + */ + FIRST_ONLY, + + /** + * Complete timeline (series of appearance & disappearance) is maintained per tuple. + */ + FAITHFUL + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java new file mode 100644 index 00000000..2777f169 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/TimelyReteBackendFactory.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.matcher; + +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.AggregatorArchitecture; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; + +/** + * A {@link ReteBackendFactory} implementation that creates {@link ReteEngine}s that use non-scattered timely + * evaluation. + * + * @author Tamas Szabo + * @since 2.4 + */ +public class TimelyReteBackendFactory extends ReteBackendFactory { + + private final TimelyConfiguration configuration; + + public static final TimelyReteBackendFactory FIRST_ONLY_SEQUENTIAL = new TimelyReteBackendFactory( + new TimelyConfiguration(TimelineRepresentation.FIRST_ONLY, AggregatorArchitecture.SEQUENTIAL)); + public static final TimelyReteBackendFactory FIRST_ONLY_PARALLEL = new TimelyReteBackendFactory( + new TimelyConfiguration(TimelineRepresentation.FIRST_ONLY, AggregatorArchitecture.PARALLEL)); + public static final TimelyReteBackendFactory FAITHFUL_SEQUENTIAL = new TimelyReteBackendFactory( + new TimelyConfiguration(TimelineRepresentation.FAITHFUL, AggregatorArchitecture.SEQUENTIAL)); + public static final TimelyReteBackendFactory FAITHFUL_PARALLEL = new TimelyReteBackendFactory( + new TimelyConfiguration(TimelineRepresentation.FAITHFUL, AggregatorArchitecture.PARALLEL)); + + public TimelyReteBackendFactory(final TimelyConfiguration configuration) { + this.configuration = configuration; + } + + @Override + public IQueryBackend create(final IQueryBackendContext context) { + return create(context, false, configuration); + } + + @Override + public int hashCode() { + return TimelyReteBackendFactory.class.hashCode(); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof TimelyReteBackendFactory)) { + return false; + } + return true; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java new file mode 100644 index 00000000..4aef0f96 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/Bag.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.misc; + +import java.util.Collection; +import java.util.LinkedList; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * @author Gabor Bergmann + * + * A bag is a container that tuples can be dumped into. Does NOT propagate updates! Optimized for small contents + * size OR positive updates only. + */ +public class Bag extends SimpleReceiver { + + public Collection contents; + + public Bag(ReteContainer reteContainer) { + super(reteContainer); + contents = new LinkedList(); + } + + @Override + public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { + if (direction == Direction.INSERT) + contents.add(updateElement); + else + contents.remove(updateElement); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java new file mode 100644 index 00000000..980d3eee --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/ConstantNode.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.misc; + +import java.util.Collection; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.StandardNode; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * Node that always contains a single constant Tuple + * + * @author Gabor Bergmann + */ +public class ConstantNode extends StandardNode { + + protected Tuple constant; + + /** + * @param constant + * will be wrapped using {@link IQueryRuntimeContext#wrapTuple(Tuple)} + */ + public ConstantNode(ReteContainer reteContainer, Tuple constant) { + super(reteContainer); + this.constant = reteContainer.getNetwork().getEngine().getRuntimeContext().wrapTuple(constant); + } + + @Override + public void pullInto(Collection collector, boolean flush) { + collector.add(constant); + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + collector.put(constant, Timestamp.INSERT_AT_ZERO_TIMELINE); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java new file mode 100644 index 00000000..efba3117 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DefaultDeltaMonitor.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.misc; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.rete.network.Network; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; + +/** + * Default configuration for DeltaMonitor. + * + * @author Gabor Bergmann + * + */ +public class DefaultDeltaMonitor extends DeltaMonitor { + + /** + * @param reteContainer + */ + public DefaultDeltaMonitor(ReteContainer reteContainer) { + super(reteContainer); + } + + /** + * @param network + */ + public DefaultDeltaMonitor(Network network) { + super(network.getHeadContainer()); + } + + @Override + public Tuple statelessConvert(Tuple tuple) { + return tuple; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java new file mode 100644 index 00000000..82b6ecda --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/DeltaMonitor.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.misc; + +import java.util.Collection; +import java.util.LinkedHashSet; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Clearable; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * A monitoring object that connects to the rete network as a receiver to reflect changes since an arbitrary state + * acknowledged by the client. Match tuples are represented by a type MatchType. + * + *

+ * Usage. If a new matching is found, it appears in the matchFoundEvents collection, and disappears when that + * particular matching cannot be found anymore. If the event of finding a match has been processed by the client, it can + * be removed manually. In this case, when a previously found matching is lost, the Tuple will appear in the + * matchLostEvents collection, and disappear upon finding the same matching again. "Matching lost" events can also be + * acknowledged by removing a Tuple from the collection. If the matching is found once again, it will return to + * matchFoundEvents. + * + *

+ * Technical notes. Does NOT propagate updates! + * + * By overriding statelessConvert(), results can be stored to a MatchType. MatchType must provide equals() and + * hashCode() reflecting its contents. The default implementation (DefaultDeltaMonitor) uses Tuple as MatchType. + * + * By overriding statelessFilter(), some tuples can be filtered. + * + * @author Gabor Bergmann + * + */ +public abstract class DeltaMonitor extends SimpleReceiver implements Clearable { + + /** + * matches that are newly found + */ + public Collection matchFoundEvents; + /** + * matches that are newly lost + */ + public Collection matchLostEvents; + + /** + * @param reteContainer + */ + public DeltaMonitor(ReteContainer reteContainer) { + super(reteContainer); + matchFoundEvents = new LinkedHashSet(); + matchLostEvents = new LinkedHashSet(); + reteContainer.registerClearable(this); + } + + // /** + // * Build a delta monitor into the head container of the network. + // * + // * @param network + // */ + // public DeltaMonitor(Network network) { + // this(network.getHeadContainer()); + // } + + /** + * Override this method to provide a lightweight, stateless filter on the tuples + * + * @param tuple + * the occurrence that is to be filtered + * @return true if this tuple should be monitored, false if ignored + */ + public boolean statelessFilter(Tuple tuple) { + return true; + } + + public abstract MatchType statelessConvert(Tuple tuple); + + @Override + public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { + if (statelessFilter(updateElement)) { + MatchType match = statelessConvert(updateElement); + if (direction == Direction.INSERT) { + if (!matchLostEvents.remove(match)) // either had before but + // lost + matchFoundEvents.add(match); // or brand-new + } else // revoke + { + if (!matchFoundEvents.remove(match)) // either never found + // in the first + // place + matchLostEvents.add(match); // or newly lost + } + } + } + + @Override + public void clear() { + matchFoundEvents.clear(); + matchLostEvents.clear(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java new file mode 100644 index 00000000..dcf9ae78 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/misc/SimpleReceiver.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.misc; + +import java.util.Collection; +import java.util.Collections; + +import tools.refinery.viatra.runtime.rete.network.BaseNode; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; + +/** + * @author Bergmann Gabor + * + */ +public abstract class SimpleReceiver extends BaseNode implements Receiver { + + protected Supplier parent = null; + /** + * @since 1.6 + */ + protected final Mailbox mailbox; + + /** + * @param reteContainer + */ + public SimpleReceiver(ReteContainer reteContainer) { + super(reteContainer); + mailbox = instantiateMailbox(); + reteContainer.registerClearable(mailbox); + } + + /** + * Instantiates the {@link Mailbox} of this receiver. + * Subclasses may override this method to provide their own mailbox implementation. + * + * @return the mailbox + * @since 2.0 + */ + protected Mailbox instantiateMailbox() { + if (this.reteContainer.isTimelyEvaluation()) { + return new TimelyMailbox(this, this.reteContainer); + } else { + return new BehaviorChangingMailbox(this, this.reteContainer); + } + } + + @Override + public Mailbox getMailbox() { + return this.mailbox; + } + + @Override + public void appendParent(Supplier supplier) { + if (parent == null) + parent = supplier; + else + throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent + + ") and cannot connect to additional parent (" + supplier + + ") as it is not a Uniqueness Enforcer Node. "); + } + + @Override + public void removeParent(Supplier supplier) { + if (parent == supplier) + parent = null; + else + throw new IllegalArgumentException("Illegal RETE edge removal: the parent of " + this + " is not " + + supplier); + } + + @Override + public Collection getParents() { + if (parent == null) + return Collections.emptySet(); + else + return Collections.singleton(parent); + } + + /** + * Disconnects this node from the network. Can be called publicly. + * + * @pre: child nodes, if any, must already be disconnected. + */ + public void disconnectFromNetwork() { + if (parent != null) + reteContainer.disconnect(parent, this); + } + + @Override + public void assignTraceInfo(TraceInfo traceInfo) { + super.assignTraceInfo(traceInfo); + if (traceInfo.propagateFromStandardNodeToSupplierParent()) + if (parent != null) + parent.acceptPropagatedTraceInfo(traceInfo); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java new file mode 100644 index 00000000..2469d6bd --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/BaseNode.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.TreeSet; + +import tools.refinery.viatra.runtime.rete.traceability.PatternTraceInfo; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; + +/** + * Base implementation for a Rete node. + * + * @author Bergmann Gabor + * + */ +public abstract class BaseNode implements Node { + + protected ReteContainer reteContainer; + protected long nodeId; + protected Object tag; + protected Set traceInfos; + + /** + * @param reteContainer + * the container to create this node in + */ + public BaseNode(ReteContainer reteContainer) { + super(); + this.reteContainer = reteContainer; + this.nodeId = reteContainer.registerNode(this); + this.traceInfos = new HashSet(); + } + + @Override + public String toString() { + if (tag != null) + return toStringCore() + "->" + getTraceInfoPatternsEnumerated() + "{" + tag.toString() + "}"; + else + return toStringCore() + "->" + getTraceInfoPatternsEnumerated(); + } + + /** + * clients should override this to append before the tag / trace indicators + */ + protected String toStringCore() { + return "[" + nodeId + "]" + getClass().getSimpleName(); + } + + @Override + public ReteContainer getContainer() { + return reteContainer; + } + + @Override + public long getNodeId() { + return nodeId; + } + + @Override + public Object getTag() { + return tag; + } + + @Override + public void setTag(Object tag) { + this.tag = tag; + } + + @Override + public Set getTraceInfos() { + return Collections.unmodifiableSet(traceInfos); + } + + @Override + public void assignTraceInfo(TraceInfo traceInfo) { + traceInfos.add(traceInfo); + traceInfo.assignNode(this); + } + + @Override + public void acceptPropagatedTraceInfo(TraceInfo traceInfo) { + assignTraceInfo(traceInfo); + } + + /** + * Descendants should use this in e.g. logging + */ + protected String getTraceInfoPatternsEnumerated() { + TreeSet patternNames = new TreeSet(); + for (TraceInfo trInfo : traceInfos) { + if (trInfo instanceof PatternTraceInfo) { + final String pName = ((PatternTraceInfo) trInfo).getPatternName(); + patternNames.add(pName); + } + } + return patternNames.toString(); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java new file mode 100644 index 00000000..6606a75d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.rete.aggregation.IndexerBasedAggregatorNode; +import tools.refinery.viatra.runtime.rete.boundary.InputConnector; +import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode; +import tools.refinery.viatra.runtime.rete.index.DualInputNode; +import tools.refinery.viatra.runtime.rete.index.Indexer; +import tools.refinery.viatra.runtime.rete.index.IterableIndexer; +import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; +import tools.refinery.viatra.runtime.rete.recipes.*; +import tools.refinery.viatra.runtime.rete.remote.Address; +import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Class responsible for connecting freshly instantiating Rete nodes to their parents. + * + * @author Bergmann Gabor + * + */ +class ConnectionFactory { + ReteContainer reteContainer; + + public ConnectionFactory(ReteContainer reteContainer) { + super(); + this.reteContainer = reteContainer; + } + + // TODO move to node implementation instead? + private boolean isStateful(ReteNodeRecipe recipe) { + return recipe instanceof ProjectionIndexerRecipe || recipe instanceof IndexerBasedAggregatorRecipe + || recipe instanceof SingleColumnAggregatorRecipe || recipe instanceof ExpressionEnforcerRecipe + || recipe instanceof TransitiveClosureRecipe || recipe instanceof ProductionRecipe + || recipe instanceof UniquenessEnforcerRecipe || recipe instanceof RelationEvaluationRecipe; + + } + + /** + * PRE: nodes for parent recipes must already be created and registered + *

+ * PRE: must not be an input node (for which {@link InputConnector} is responsible) + */ + public void connectToParents(RecipeTraceInfo recipeTrace, Node freshNode) { + final ReteNodeRecipe recipe = recipeTrace.getRecipe(); + if (recipe instanceof ConstantRecipe) { + // NO-OP + } else if (recipe instanceof InputRecipe) { + throw new IllegalArgumentException( + ConnectionFactory.class.getSimpleName() + " not intended for input connection: " + recipe); + } else if (recipe instanceof SingleParentNodeRecipe) { + final Receiver receiver = (Receiver) freshNode; + ReteNodeRecipe parentRecipe = ((SingleParentNodeRecipe) recipe).getParent(); + connectToParent(recipe, receiver, parentRecipe); + } else if (recipe instanceof RelationEvaluationRecipe) { + List parentRecipes = ((MultiParentNodeRecipe) recipe).getParents(); + List parentSuppliers = new ArrayList(); + for (final ReteNodeRecipe parentRecipe : parentRecipes) { + parentSuppliers.add(getSupplierForRecipe(parentRecipe)); + } + ((RelationEvaluatorNode) freshNode).connectToParents(parentSuppliers); + } else if (recipe instanceof BetaRecipe) { + final DualInputNode beta = (DualInputNode) freshNode; + final ArrayList parentTraces = new ArrayList( + recipeTrace.getParentRecipeTraces()); + Slots slots = avoidActiveNodeConflict(parentTraces.get(0), parentTraces.get(1)); + beta.connectToIndexers(slots.primary, slots.secondary); + } else if (recipe instanceof IndexerBasedAggregatorRecipe) { + final IndexerBasedAggregatorNode aggregator = (IndexerBasedAggregatorNode) freshNode; + final IndexerBasedAggregatorRecipe aggregatorRecipe = (IndexerBasedAggregatorRecipe) recipe; + aggregator.initializeWith((ProjectionIndexer) resolveIndexer(aggregatorRecipe.getParent())); + } else if (recipe instanceof MultiParentNodeRecipe) { + final Receiver receiver = (Receiver) freshNode; + List parentRecipes = ((MultiParentNodeRecipe) recipe).getParents(); + for (ReteNodeRecipe parentRecipe : parentRecipes) { + connectToParent(recipe, receiver, parentRecipe); + } + } + } + + private Indexer resolveIndexer(final IndexerRecipe indexerRecipe) { + final Address address = reteContainer.getNetwork().getExistingNodeByRecipe(indexerRecipe); + return (Indexer) reteContainer.resolveLocal(address); + } + + private void connectToParent(ReteNodeRecipe recipe, Receiver freshNode, ReteNodeRecipe parentRecipe) { + final Supplier parentSupplier = getSupplierForRecipe(parentRecipe); + + // special synch + if (freshNode instanceof ReinitializedNode) { + Collection tuples = new ArrayList(); + parentSupplier.pullInto(tuples, true); + ((ReinitializedNode) freshNode).reinitializeWith(tuples); + reteContainer.connect(parentSupplier, freshNode); + } else { // default case + // stateless nodes do not have to be synced with contents UNLESS they already have children (recursive + // corner case) + if (isStateful(recipe) + || ((freshNode instanceof Supplier) && !((Supplier) freshNode).getReceivers().isEmpty())) { + reteContainer.connectAndSynchronize(parentSupplier, freshNode); + } else { + // stateless node, no synch + reteContainer.connect(parentSupplier, freshNode); + } + } + } + + private Supplier getSupplierForRecipe(ReteNodeRecipe recipe) { + @SuppressWarnings("unchecked") + final Address parentAddress = (Address) reteContainer.getNetwork() + .getExistingNodeByRecipe(recipe); + final Supplier supplier = reteContainer.getProvisioner().asSupplier(parentAddress); + return supplier; + } + + /** + * If two indexers share their active node, joining them via DualInputNode is error-prone. Exception: coincidence of + * the two indexers is supported. + * + * @return a replacement for the secondary Indexers, if needed + */ + private Slots avoidActiveNodeConflict(final RecipeTraceInfo primarySlot, final RecipeTraceInfo secondarySlot) { + Slots result = new Slots() { + { + primary = (IterableIndexer) resolveIndexer((ProjectionIndexerRecipe) primarySlot.getRecipe()); + secondary = resolveIndexer((IndexerRecipe) secondarySlot.getRecipe()); + } + }; + if (activeNodeConflict(result.primary, result.secondary)) + if (result.secondary instanceof IterableIndexer) + result.secondary = resolveActiveIndexer(secondarySlot); + else + result.primary = (IterableIndexer) resolveActiveIndexer(primarySlot); + return result; + } + + private Indexer resolveActiveIndexer(final RecipeTraceInfo inactiveIndexerTrace) { + final RecipeTraceInfo activeIndexerTrace = reteContainer.getProvisioner() + .accessActiveIndexer(inactiveIndexerTrace); + reteContainer.getProvisioner().getOrCreateNodeByRecipe(activeIndexerTrace); + return resolveIndexer((ProjectionIndexerRecipe) activeIndexerTrace.getRecipe()); + } + + private static class Slots { + IterableIndexer primary; + Indexer secondary; + } + + /** + * If two indexers share their active node, joining them via DualInputNode is error-prone. Exception: coincidence of + * the two indexers is supported. + * + * @return true if there is a conflict of active nodes. + */ + private boolean activeNodeConflict(Indexer primarySlot, Indexer secondarySlot) { + return !primarySlot.equals(secondarySlot) && primarySlot.getActiveNode().equals(secondarySlot.getActiveNode()); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java new file mode 100644 index 00000000..c22b06d8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/IGroupable.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network; + +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; + +/** + * @author Gabor Bergmann + * @since 1.7 + */ +public interface IGroupable { + + /** + * @return the current group of the mailbox + * @since 1.7 + */ + CommunicationGroup getCurrentGroup(); + + /** + * Sets the current group of the mailbox + * @since 1.7 + */ + void setCurrentGroup(CommunicationGroup group); + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java new file mode 100644 index 00000000..64f59ff3 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Network.java @@ -0,0 +1,408 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.network; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.boundary.InputConnector; +import tools.refinery.viatra.runtime.rete.matcher.ReteEngine; +import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; +import tools.refinery.viatra.runtime.rete.remote.Address; +import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; +import tools.refinery.viatra.runtime.rete.util.Options; + +/** + * @author Gabor Bergmann + * + */ +public class Network { + final int threads; + + protected ArrayList containers; + ReteContainer headContainer; + private int firstContainer = 0; + private int nextContainer = 0; + + // the following fields exist only if threads > 0 + protected Map globalTerminationCriteria = null; + protected Map reportedClocks = null; + protected Lock updateLock = null; // grab during normal update operations + protected Lock structuralChangeLock = null; // grab if the network structure + // is to + // be changed + + // Knowledge of the outside world + private ReteEngine engine; + protected NodeFactory nodeFactory; + protected InputConnector inputConnector; + + // Node and recipe administration + // incl. addresses for existing nodes by recipe (where available) + // Maintained by NodeProvisioner of each container + Map> nodesByRecipe = CollectionsFactory.createMap(); + Set recipeTraces = CollectionsFactory.createSet(); + + /** + * @throws IllegalStateException + * if no node has been constructed for the recipe + */ + public synchronized Address getExistingNodeByRecipe(ReteNodeRecipe recipe) { + final Address node = nodesByRecipe.get(recipe); + if (node == null) + throw new IllegalStateException(String.format("Rete node for recipe %s not constructed yet.", recipe)); + return node; + } + + /** + * @return null if no node has been constructed for the recipe + */ + public synchronized Address getNodeByRecipeIfExists(ReteNodeRecipe recipe) { + final Address node = nodesByRecipe.get(recipe); + return node; + } + + /** + * @param threads + * the number of threads to operate the network with; 0 means single-threaded operation, 1 starts an + * asynchronous thread to operate the RETE net, >1 uses multiple RETE containers. + */ + public Network(int threads, ReteEngine engine) { + super(); + this.threads = threads; + this.engine = engine; + this.inputConnector = new InputConnector(this); + this.nodeFactory = new NodeFactory(engine.getLogger()); + + containers = new ArrayList(); + firstContainer = (threads > 1) ? Options.firstFreeContainer : 0; // NOPMD + nextContainer = firstContainer; + + if (threads > 0) { + globalTerminationCriteria = CollectionsFactory.createMap(); + reportedClocks = CollectionsFactory.createMap(); + ReadWriteLock rwl = new ReentrantReadWriteLock(); + updateLock = rwl.readLock(); + structuralChangeLock = rwl.writeLock(); + for (int i = 0; i < threads; ++i) + containers.add(new ReteContainer(this, true)); + } else + containers.add(new ReteContainer(this, false)); + + headContainer = containers.get(0); + } + + /** + * Kills this Network along with all containers and message consumption cycles. + */ + public void kill() { + for (ReteContainer container : containers) { + container.kill(); + } + containers.clear(); + } + + /** + * Returns the head container, that is guaranteed to reside in the same JVM as the Network object. + */ + public ReteContainer getHeadContainer() { + return headContainer; + } + + /** + * Returns the next container in round-robin fashion. Configurable not to yield head container. + */ + public ReteContainer getNextContainer() { + if (nextContainer >= containers.size()) + nextContainer = firstContainer; + return containers.get(nextContainer++); + } + + /** + * Internal message delivery method. + * + * @pre threads > 0 + */ + private void sendUpdate(Address receiver, Direction direction, Tuple updateElement) { + ReteContainer affectedContainer = receiver.getContainer(); + synchronized (globalTerminationCriteria) { + long newCriterion = affectedContainer.sendUpdateToLocalAddress(receiver, direction, updateElement); + terminationCriterion(affectedContainer, newCriterion); + } + } + + /** + * Internal message delivery method for single-threaded operation + * + * @pre threads == 0 + */ + private void sendUpdateSingleThreaded(Address receiver, Direction direction, + Tuple updateElement) { + ReteContainer affectedContainer = receiver.getContainer(); + affectedContainer.sendUpdateToLocalAddressSingleThreaded(receiver, direction, updateElement); + } + + /** + * Internal message delivery method. + * + * @pre threads > 0 + */ + private void sendUpdates(Address receiver, Direction direction, + Collection updateElements) { + if (updateElements.isEmpty()) + return; + ReteContainer affectedContainer = receiver.getContainer(); + synchronized (globalTerminationCriteria) { + long newCriterion = affectedContainer.sendUpdatesToLocalAddress(receiver, direction, updateElements); + terminationCriterion(affectedContainer, newCriterion); + } + } + + /** + * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The node may + * reside in any of the containers associated with this network. To be called from a user thread during normal + * operation, NOT during construction. + * + * @since 2.4 + */ + public void sendExternalUpdate(Address receiver, Direction direction, Tuple updateElement) { + if (threads > 0) { + try { + updateLock.lock(); + sendUpdate(receiver, direction, updateElement); + } finally { + updateLock.unlock(); + } + } else { + sendUpdateSingleThreaded(receiver, direction, updateElement); + // getHeadContainer(). + } + } + + /** + * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The node may + * reside in any of the containers associated with this network. To be called from a user thread during + * construction. + * + * @pre: structuralChangeLock MUST be grabbed by the sequence (but not necessarily this thread, as the sequence may + * span through network calls, that's why it's not enforced here ) + * + * @return the value of the target container's clock at the time when the message was accepted into its message + * queue + * @since 2.4 + */ + public void sendConstructionUpdate(Address receiver, Direction direction, Tuple updateElement) { + // structuralChangeLock.lock(); + if (threads > 0) + sendUpdate(receiver, direction, updateElement); + else + receiver.getContainer().sendUpdateToLocalAddressSingleThreaded(receiver, direction, updateElement); + // structuralChangeLock.unlock(); + } + + /** + * Sends multiple update messages atomically to the receiver node, indicating a newly found or lost partial + * matching. The node may reside in any of the containers associated with this network. To be called from a user + * thread during construction. + * + * @pre: structuralChangeLock MUST be grabbed by the sequence (but not necessarily this thread, as the sequence may + * span through network calls, that's why it's not enforced here ) + * + * @since 2.4 + */ + public void sendConstructionUpdates(Address receiver, Direction direction, + Collection updateElements) { + // structuralChangeLock.lock(); + if (threads > 0) + sendUpdates(receiver, direction, updateElements); + else + receiver.getContainer().sendUpdatesToLocalAddressSingleThreaded(receiver, direction, updateElements); + // structuralChangeLock.unlock(); + } + + /** + * Establishes connection between a supplier and a receiver node, regardless which container they are in. Not to be + * called remotely, because this method enforces the structural lock. + * + * @param supplier + * @param receiver + * @param synchronise + * indicates whether the receiver should be synchronised to the current contents of the supplier + */ + public void connectRemoteNodes(Address supplier, Address receiver, + boolean synchronise) { + try { + if (threads > 0) + structuralChangeLock.lock(); + receiver.getContainer().connectRemoteNodes(supplier, receiver, synchronise); + } finally { + if (threads > 0) + structuralChangeLock.unlock(); + } + } + + /** + * Severs connection between a supplier and a receiver node, regardless which container they are in. Not to be + * called remotely, because this method enforces the structural lock. + * + * @param supplier + * @param receiver + * @param desynchronise + * indicates whether the current contents of the supplier should be subtracted from the receiver + */ + public void disconnectRemoteNodes(Address supplier, Address receiver, + boolean desynchronise) { + try { + if (threads > 0) + structuralChangeLock.lock(); + receiver.getContainer().disconnectRemoteNodes(supplier, receiver, desynchronise); + } finally { + if (threads > 0) + structuralChangeLock.unlock(); + } + } + + /** + * Containers use this method to report whenever they run out of messages in their queue. + * + * To be called from the thread of the reporting container. + * + * @pre threads > 0. + * @param reportingContainer + * the container reporting the emptiness of its message queue. + * @param clock + * the value of the container's clock when reporting. + * @param localTerminationCriteria + * the latest clock values this container has received from other containers since the last time it + * reported termination. + */ + void reportLocalUpdateTermination(ReteContainer reportingContainer, long clock, + Map localTerminationCriteria) { + synchronized (globalTerminationCriteria) { + for (Entry criterion : localTerminationCriteria.entrySet()) { + terminationCriterion(criterion.getKey(), criterion.getValue()); + } + + reportedClocks.put(reportingContainer, clock); + Long criterion = globalTerminationCriteria.get(reportingContainer); + if (criterion != null && criterion < clock) + globalTerminationCriteria.remove(reportingContainer); + + if (globalTerminationCriteria.isEmpty()) + globalTerminationCriteria.notifyAll(); + } + } + + /** + * @pre threads > 0 + */ + private void terminationCriterion(ReteContainer affectedContainer, long newCriterion) { + synchronized (globalTerminationCriteria) { + Long oldCriterion = globalTerminationCriteria.get(affectedContainer); + Long oldClock = reportedClocks.get(affectedContainer); + long relevantClock = oldClock == null ? 0 : oldClock; + if ((relevantClock <= newCriterion) && (oldCriterion == null || oldCriterion < newCriterion)) { + globalTerminationCriteria.put(affectedContainer, newCriterion); + } + } + } + + /** + * Waits until all rete update operations are settled in all containers. Returns immediately, if no updates are + * pending. + * + * To be called from any user thread. + */ + public void waitForReteTermination() { + if (threads > 0) { + synchronized (globalTerminationCriteria) { + while (!globalTerminationCriteria.isEmpty()) { + try { + globalTerminationCriteria.wait(); + } catch (InterruptedException e) { + + } + } + } + } else + headContainer.deliverMessagesSingleThreaded(); + } + + /** + * Waits to execute action until all rete update operations are settled in all containers. Runs action and returns + * immediately, if no updates are pending. The given action is guaranteed to be run when the terminated state still + * persists. + * + * @param action + * the action to be run when reaching the steady-state. + * + * To be called from any user thread. + */ + public void waitForReteTermination(Runnable action) { + if (threads > 0) { + synchronized (globalTerminationCriteria) { + while (!globalTerminationCriteria.isEmpty()) { + try { + globalTerminationCriteria.wait(); + } catch (InterruptedException e) { + + } + } + action.run(); + } + } else { + headContainer.deliverMessagesSingleThreaded(); + action.run(); + } + + } + + /** + * @return an unmodifiable set of known recipe traces + */ + public Set getRecipeTraces() { + return Collections.unmodifiableSet(recipeTraces); + } + + /** + * @return an unmodifiable list of containers + */ + public List getContainers() { + return Collections.unmodifiableList(containers); + } + + public Lock getStructuralChangeLock() { + return structuralChangeLock; + } + + public NodeFactory getNodeFactory() { + return nodeFactory; + } + + public InputConnector getInputConnector() { + return inputConnector; + } + + public ReteEngine getEngine() { + return engine; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java new file mode 100644 index 00000000..c6ba34c4 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NetworkStructureChangeSensitiveNode.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network; + +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; + +/** + * {@link Node}s implementing this interface are sensitive to changes in the dependency graph maintained by the + * {@link CommunicationTracker}. The {@link CommunicationTracker} notifies these nodes whenever the SCC of this node is + * affected by changes to the dependency graph. Depending on whether this node is contained in a recursive group or not, + * it may behave differently, and the {@link NetworkStructureChangeSensitiveNode#networkStructureChanged()} method can + * be used to perform changes in behavior. + * + * @author Tamas Szabo + * @since 2.3 + */ +public interface NetworkStructureChangeSensitiveNode extends Node { + + /** + * At the time of the invocation, the dependency graph has already been updated. + */ + public void networkStructureChanged(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java new file mode 100644 index 00000000..e8ab615a --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Node.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.network; + +import java.util.Set; + +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; + +/** + * A node of a rete network, should be uniquely identified by network and nodeId. NodeId can be requested by registering + * at the Network on construction. + * + * @author Gabor Bergmann + */ +public interface Node { + /** + * @return the network this node belongs to. + */ + ReteContainer getContainer(); + + /** + * @return the identifier unique to this node within the network. + */ + long getNodeId(); + + /** + * Assigns a descriptive tag to the node + */ + void setTag(Object tag); + + /** + * @return the tag of the node + */ + Object getTag(); + + /** + * @return unmodifiable view of the list of traceability infos assigned to this node + */ + Set getTraceInfos(); + + /** + * assigns new traceability info to this node + */ + void assignTraceInfo(TraceInfo traceInfo); + /** + * accepts traceability info propagated to this node + */ + void acceptPropagatedTraceInfo(TraceInfo traceInfo); + + default CommunicationTracker getCommunicationTracker() { + return getContainer().getCommunicationTracker(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java new file mode 100644 index 00000000..a40a8b7f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java @@ -0,0 +1,375 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network; + +import org.apache.log4j.Logger; +import org.eclipse.emf.common.util.EMap; +import tools.refinery.viatra.runtime.base.itc.alg.representative.RepresentativeElectionAlgorithm; +import tools.refinery.viatra.runtime.base.itc.alg.representative.StronglyConnectedComponentAlgorithm; +import tools.refinery.viatra.runtime.base.itc.alg.representative.WeaklyConnectedComponentAlgorithm; +import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; +import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; +import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.rete.aggregation.ColumnAggregatorNode; +import tools.refinery.viatra.runtime.rete.aggregation.CountNode; +import tools.refinery.viatra.runtime.rete.aggregation.IAggregatorNode; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulSequentialTimelyColumnAggregatorNode; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FirstOnlyParallelTimelyColumnAggregatorNode; +import tools.refinery.viatra.runtime.rete.aggregation.timely.FirstOnlySequentialTimelyColumnAggregatorNode; +import tools.refinery.viatra.runtime.rete.boundary.ExternalInputEnumeratorNode; +import tools.refinery.viatra.runtime.rete.boundary.ExternalInputStatelessFilterNode; +import tools.refinery.viatra.runtime.rete.eval.EvaluatorCore; +import tools.refinery.viatra.runtime.rete.eval.MemorylessEvaluatorNode; +import tools.refinery.viatra.runtime.rete.eval.OutputCachingEvaluatorNode; +import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode; +import tools.refinery.viatra.runtime.rete.index.ExistenceNode; +import tools.refinery.viatra.runtime.rete.index.Indexer; +import tools.refinery.viatra.runtime.rete.index.JoinNode; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.AggregatorArchitecture; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; +import tools.refinery.viatra.runtime.rete.misc.ConstantNode; +import tools.refinery.viatra.runtime.rete.recipes.*; +import tools.refinery.viatra.runtime.rete.single.*; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Factory for instantiating Rete nodes. The created nodes are not connected to the network yet. + * + * @author Bergmann Gabor + * + */ +class NodeFactory { + Logger logger; + + public NodeFactory(Logger logger) { + super(); + this.logger = logger; + } + + /** + * PRE: parent node must already be created + */ + public Indexer createIndexer(ReteContainer reteContainer, IndexerRecipe recipe, Supplier parentNode, + TraceInfo... traces) { + + if (recipe instanceof ProjectionIndexerRecipe) { + return parentNode.constructIndex(toMask(recipe.getMask()), traces); + // already traced + } else if (recipe instanceof AggregatorIndexerRecipe) { + int indexOfAggregateResult = recipe.getParent().getArity(); + int resultPosition = recipe.getMask().getSourceIndices().lastIndexOf(indexOfAggregateResult); + + IAggregatorNode aggregatorNode = (IAggregatorNode) parentNode; + final Indexer result = (resultPosition == -1) ? aggregatorNode.getAggregatorOuterIndexer() + : aggregatorNode.getAggregatorOuterIdentityIndexer(resultPosition); + + for (TraceInfo traceInfo : traces) + result.assignTraceInfo(traceInfo); + return result; + } else + throw new IllegalArgumentException("Unkown Indexer recipe: " + recipe); + } + + /** + * PRE: recipe is not an indexer recipe. + */ + public Supplier createNode(ReteContainer reteContainer, ReteNodeRecipe recipe, TraceInfo... traces) { + if (recipe instanceof IndexerRecipe) + throw new IllegalArgumentException("Indexers are not created by NodeFactory: " + recipe); + + Supplier result = instantiateNodeDispatch(reteContainer, recipe); + for (TraceInfo traceInfo : traces) + result.assignTraceInfo(traceInfo); + return result; + } + + private Supplier instantiateNodeDispatch(ReteContainer reteContainer, ReteNodeRecipe recipe) { + + // Parentless + + if (recipe instanceof ConstantRecipe) + return instantiateNode(reteContainer, (ConstantRecipe) recipe); + if (recipe instanceof InputRecipe) + return instantiateNode(reteContainer, (InputRecipe) recipe); + + // SingleParentNodeRecipe + + // if (recipe instanceof ProjectionIndexer) + // return instantiateNode((ProjectionIndexer)recipe); + if (recipe instanceof InputFilterRecipe) + return instantiateNode(reteContainer, (InputFilterRecipe) recipe); + if (recipe instanceof InequalityFilterRecipe) + return instantiateNode(reteContainer, (InequalityFilterRecipe) recipe); + if (recipe instanceof EqualityFilterRecipe) + return instantiateNode(reteContainer, (EqualityFilterRecipe) recipe); + if (recipe instanceof TransparentRecipe) + return instantiateNode(reteContainer, (TransparentRecipe) recipe); + if (recipe instanceof TrimmerRecipe) + return instantiateNode(reteContainer, (TrimmerRecipe) recipe); + if (recipe instanceof TransitiveClosureRecipe) + return instantiateNode(reteContainer, (TransitiveClosureRecipe) recipe); + if (recipe instanceof RepresentativeElectionRecipe) + return instantiateNode(reteContainer, (RepresentativeElectionRecipe) recipe); + if (recipe instanceof RelationEvaluationRecipe) + return instantiateNode(reteContainer, (RelationEvaluationRecipe) recipe); + if (recipe instanceof ExpressionEnforcerRecipe) + return instantiateNode(reteContainer, (ExpressionEnforcerRecipe) recipe); + if (recipe instanceof CountAggregatorRecipe) + return instantiateNode(reteContainer, (CountAggregatorRecipe) recipe); + if (recipe instanceof SingleColumnAggregatorRecipe) + return instantiateNode(reteContainer, (SingleColumnAggregatorRecipe) recipe); + if (recipe instanceof DiscriminatorDispatcherRecipe) + return instantiateNode(reteContainer, (DiscriminatorDispatcherRecipe) recipe); + if (recipe instanceof DiscriminatorBucketRecipe) + return instantiateNode(reteContainer, (DiscriminatorBucketRecipe) recipe); + + // MultiParentNodeRecipe + if (recipe instanceof UniquenessEnforcerRecipe) + return instantiateNode(reteContainer, (UniquenessEnforcerRecipe) recipe); + if (recipe instanceof ProductionRecipe) + return instantiateNode(reteContainer, (ProductionRecipe) recipe); + + // BetaNodeRecipe + if (recipe instanceof JoinRecipe) + return instantiateNode(reteContainer, (JoinRecipe) recipe); + if (recipe instanceof SemiJoinRecipe) + return instantiateNode(reteContainer, (SemiJoinRecipe) recipe); + if (recipe instanceof AntiJoinRecipe) + return instantiateNode(reteContainer, (AntiJoinRecipe) recipe); + + // ... else + throw new IllegalArgumentException("Unsupported recipe type: " + recipe); + } + + // INSTANTIATION for recipe types + + private Supplier instantiateNode(ReteContainer reteContainer, InputRecipe recipe) { + return new ExternalInputEnumeratorNode(reteContainer); + } + + private Supplier instantiateNode(ReteContainer reteContainer, InputFilterRecipe recipe) { + return new ExternalInputStatelessFilterNode(reteContainer, toMaskOrNull(recipe.getMask())); + } + + private Supplier instantiateNode(ReteContainer reteContainer, CountAggregatorRecipe recipe) { + return new CountNode(reteContainer); + } + + private Supplier instantiateNode(ReteContainer reteContainer, TransparentRecipe recipe) { + return new TransparentNode(reteContainer); + } + + private Supplier instantiateNode(ReteContainer reteContainer, ExpressionEnforcerRecipe recipe) { + final IExpressionEvaluator evaluator = toIExpressionEvaluator(recipe.getExpression()); + final Map posMapping = toStringIndexMap(recipe.getMappedIndices()); + final int sourceTupleWidth = recipe.getParent().getArity(); + EvaluatorCore core = null; + if (recipe instanceof CheckRecipe) { + core = new EvaluatorCore.PredicateEvaluatorCore(logger, evaluator, posMapping, sourceTupleWidth); + } else if (recipe instanceof EvalRecipe) { + final boolean isUnwinding = ((EvalRecipe) recipe).isUnwinding(); + core = new EvaluatorCore.FunctionEvaluatorCore(logger, evaluator, posMapping, sourceTupleWidth, isUnwinding); + } else { + throw new IllegalArgumentException("Unhandled expression enforcer recipe: " + recipe.getClass() + "!"); + } + if (recipe.isCacheOutput()) { + return new OutputCachingEvaluatorNode(reteContainer, core); + } else { + return new MemorylessEvaluatorNode(reteContainer, core); + } + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private Supplier instantiateNode(ReteContainer reteContainer, SingleColumnAggregatorRecipe recipe) { + final IMultisetAggregationOperator operator = recipe.getMultisetAggregationOperator(); + TupleMask coreMask = null; + if (recipe.getOptionalMonotonicityInfo() != null) { + coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask()); + } else { + coreMask = toMask(recipe.getGroupByMask()); + } + + if (reteContainer.isTimelyEvaluation()) { + final TimelyConfiguration timelyConfiguration = reteContainer.getTimelyConfiguration(); + final AggregatorArchitecture aggregatorArchitecture = timelyConfiguration.getAggregatorArchitecture(); + final TimelineRepresentation timelineRepresentation = timelyConfiguration.getTimelineRepresentation(); + + TupleMask posetMask = null; + + if (recipe.getOptionalMonotonicityInfo() != null) { + posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask()); + } else { + final int aggregatedColumn = recipe.getAggregableIndex(); + posetMask = TupleMask.selectSingle(aggregatedColumn, coreMask.sourceWidth); + } + + if (timelineRepresentation == TimelineRepresentation.FIRST_ONLY + && aggregatorArchitecture == AggregatorArchitecture.SEQUENTIAL) { + return new FirstOnlySequentialTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask); + } else if (timelineRepresentation == TimelineRepresentation.FIRST_ONLY + && aggregatorArchitecture == AggregatorArchitecture.PARALLEL) { + return new FirstOnlyParallelTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask); + } else if (timelineRepresentation == TimelineRepresentation.FAITHFUL + && aggregatorArchitecture == AggregatorArchitecture.SEQUENTIAL) { + return new FaithfulSequentialTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask); + } else if (timelineRepresentation == TimelineRepresentation.FAITHFUL + && aggregatorArchitecture == AggregatorArchitecture.PARALLEL) { + return new FaithfulParallelTimelyColumnAggregatorNode(reteContainer, operator, coreMask, posetMask); + } else { + throw new IllegalArgumentException("Unsupported timely configuration!"); + } + } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) { + final TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask()); + final IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo() + .getPosetComparator(); + return new ColumnAggregatorNode(reteContainer, operator, recipe.isDeleteRederiveEvaluation(), coreMask, + posetMask, posetComparator); + } else { + final int aggregatedColumn = recipe.getAggregableIndex(); + return new ColumnAggregatorNode(reteContainer, operator, coreMask, aggregatedColumn); + } + } + + private Supplier instantiateNode(ReteContainer reteContainer, TransitiveClosureRecipe recipe) { + return new TransitiveClosureNode(reteContainer); + } + + private Supplier instantiateNode(ReteContainer reteContainer, RepresentativeElectionRecipe recipe) { + RepresentativeElectionAlgorithm.Factory algorithmFactory = switch (recipe.getConnectivity()) { + case STRONG -> StronglyConnectedComponentAlgorithm::new; + case WEAK -> WeaklyConnectedComponentAlgorithm::new; + }; + return new RepresentativeElectionNode(reteContainer, algorithmFactory); + } + + private Supplier instantiateNode(ReteContainer reteContainer, RelationEvaluationRecipe recipe) { + return new RelationEvaluatorNode(reteContainer, toIRelationEvaluator(recipe.getEvaluator())); + } + + private Supplier instantiateNode(ReteContainer reteContainer, ProductionRecipe recipe) { + if (reteContainer.isTimelyEvaluation()) { + return new TimelyProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices())); + } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) { + TupleMask coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask()); + TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask()); + IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo() + .getPosetComparator(); + return new DefaultProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices()), + recipe.isDeleteRederiveEvaluation(), coreMask, posetMask, posetComparator); + } else { + return new DefaultProductionNode(reteContainer, toStringIndexMap(recipe.getMappedIndices()), + recipe.isDeleteRederiveEvaluation()); + } + } + + private Supplier instantiateNode(ReteContainer reteContainer, UniquenessEnforcerRecipe recipe) { + if (reteContainer.isTimelyEvaluation()) { + return new TimelyUniquenessEnforcerNode(reteContainer, recipe.getArity()); + } else if (recipe.isDeleteRederiveEvaluation() && recipe.getOptionalMonotonicityInfo() != null) { + TupleMask coreMask = toMask(recipe.getOptionalMonotonicityInfo().getCoreMask()); + TupleMask posetMask = toMask(recipe.getOptionalMonotonicityInfo().getPosetMask()); + IPosetComparator posetComparator = (IPosetComparator) recipe.getOptionalMonotonicityInfo() + .getPosetComparator(); + return new UniquenessEnforcerNode(reteContainer, recipe.getArity(), recipe.isDeleteRederiveEvaluation(), + coreMask, posetMask, posetComparator); + } else { + return new UniquenessEnforcerNode(reteContainer, recipe.getArity(), recipe.isDeleteRederiveEvaluation()); + } + } + + private Supplier instantiateNode(ReteContainer reteContainer, ConstantRecipe recipe) { + final List constantValues = recipe.getConstantValues(); + final Object[] constantArray = constantValues.toArray(new Object[constantValues.size()]); + return new ConstantNode(reteContainer, Tuples.flatTupleOf(constantArray)); + } + + private Supplier instantiateNode(ReteContainer reteContainer, DiscriminatorBucketRecipe recipe) { + return new DiscriminatorBucketNode(reteContainer, recipe.getBucketKey()); + } + + private Supplier instantiateNode(ReteContainer reteContainer, DiscriminatorDispatcherRecipe recipe) { + return new DiscriminatorDispatcherNode(reteContainer, recipe.getDiscriminationColumnIndex()); + } + + private Supplier instantiateNode(ReteContainer reteContainer, TrimmerRecipe recipe) { + return new TrimmerNode(reteContainer, toMask(recipe.getMask())); + } + + private Supplier instantiateNode(ReteContainer reteContainer, InequalityFilterRecipe recipe) { + Tunnel result = new InequalityFilterNode(reteContainer, recipe.getSubject(), + TupleMask.fromSelectedIndices(recipe.getParent().getArity(), recipe.getInequals())); + return result; + } + + private Supplier instantiateNode(ReteContainer reteContainer, EqualityFilterRecipe recipe) { + final int[] equalIndices = TupleMask.integersToIntArray(recipe.getIndices()); + return new EqualityFilterNode(reteContainer, equalIndices); + } + + private Supplier instantiateNode(ReteContainer reteContainer, AntiJoinRecipe recipe) { + return new ExistenceNode(reteContainer, true); + } + + private Supplier instantiateNode(ReteContainer reteContainer, SemiJoinRecipe recipe) { + return new ExistenceNode(reteContainer, false); + } + + private Supplier instantiateNode(ReteContainer reteContainer, JoinRecipe recipe) { + return new JoinNode(reteContainer, toMask(recipe.getRightParentComplementaryMask())); + } + + // HELPERS + + private IExpressionEvaluator toIExpressionEvaluator(ExpressionDefinition expressionDefinition) { + final Object evaluator = expressionDefinition.getEvaluator(); + if (evaluator instanceof IExpressionEvaluator) { + return (IExpressionEvaluator) evaluator; + } + throw new IllegalArgumentException("No runtime support for expression evaluator: " + evaluator); + } + + private IRelationEvaluator toIRelationEvaluator(ExpressionDefinition expressionDefinition) { + final Object evaluator = expressionDefinition.getEvaluator(); + if (evaluator instanceof IRelationEvaluator) { + return (IRelationEvaluator) evaluator; + } + throw new IllegalArgumentException("No runtime support for relation evaluator: " + evaluator); + } + + private Map toStringIndexMap(final EMap mappedIndices) { + final HashMap result = new HashMap(); + for (java.util.Map.Entry entry : mappedIndices) { + result.put(entry.getKey(), entry.getValue()); + } + return result; + } + + /** Mask can be null */ + private TupleMask toMaskOrNull(Mask mask) { + if (mask == null) + return null; + else + return toMask(mask); + } + + /** Mask is non-null. */ + private TupleMask toMask(Mask mask) { + return TupleMask.fromSelectedIndices(mask.getSourceArity(), mask.getSourceIndices()); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java new file mode 100644 index 00000000..9121fc44 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeProvisioner.java @@ -0,0 +1,346 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.network; + +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.rete.boundary.InputConnector; +import tools.refinery.viatra.runtime.rete.construction.plancompiler.CompilerHelper; +import tools.refinery.viatra.runtime.rete.index.Indexer; +import tools.refinery.viatra.runtime.rete.index.OnetimeIndexer; +import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; +import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand; +import tools.refinery.viatra.runtime.rete.recipes.*; +import tools.refinery.viatra.runtime.rete.recipes.helper.RecipeRecognizer; +import tools.refinery.viatra.runtime.rete.recipes.helper.RecipesHelper; +import tools.refinery.viatra.runtime.rete.remote.Address; +import tools.refinery.viatra.runtime.rete.remote.RemoteReceiver; +import tools.refinery.viatra.runtime.rete.remote.RemoteSupplier; +import tools.refinery.viatra.runtime.rete.traceability.ActiveNodeConflictTrace; +import tools.refinery.viatra.runtime.rete.traceability.RecipeTraceInfo; +import tools.refinery.viatra.runtime.rete.traceability.UserRequestTrace; +import tools.refinery.viatra.runtime.rete.util.Options; + +import java.util.Map; +import java.util.Set; + +/** + * Stores the internal parts of a rete network. Nodes are stored according to type and parameters. + * + * @author Gabor Bergmann + */ +public class NodeProvisioner { + + // boolean activeStorage = true; + + ReteContainer reteContainer; + NodeFactory nodeFactory; + ConnectionFactory connectionFactory; + InputConnector inputConnector; + IQueryRuntimeContext runtimeContext; + + // TODO as recipe? + Map remoteReceivers = CollectionsFactory.createMap(); + Map, RemoteSupplier> remoteSuppliers = CollectionsFactory.createMap(); + + private RecipeRecognizer recognizer; + + /** + * PRE: NodeFactory, ConnectionFactory must exist + * + * @param reteContainer + * the ReteNet whose interior is to be mapped. + */ + public NodeProvisioner(ReteContainer reteContainer) { + super(); + this.reteContainer = reteContainer; + this.nodeFactory = reteContainer.getNodeFactory(); + this.connectionFactory = reteContainer.getConnectionFactory(); + this.inputConnector = reteContainer.getInputConnectionFactory(); + runtimeContext = reteContainer.getNetwork().getEngine().getRuntimeContext(); + recognizer = new RecipeRecognizer(runtimeContext); + } + + public synchronized Address getOrCreateNodeByRecipe(RecipeTraceInfo recipeTrace) { + ReteNodeRecipe recipe = recipeTrace.getRecipe(); + Address result = getNodesByRecipe().get(recipe); + if (result != null) { + // NODE ALREADY CONSTRUCTED FOR RECIPE, only needs to add trace + if (getRecipeTraces().add(recipeTrace)) + result.getNodeCache().assignTraceInfo(recipeTrace); + } else { + // No node for this recipe object - but equivalent recipes still + // reusable + ReteNodeRecipe canonicalRecipe = recognizer.canonicalizeRecipe(recipe); + if (canonicalRecipe != recipe) { + // FOUND EQUIVALENT RECIPE + result = getNodesByRecipe().get(canonicalRecipe); + if (result != null) { + // NODE ALREADY CONSTRUCTED FOR EQUIVALENT RECIPE + recipeTrace.shadowWithEquivalentRecipe(canonicalRecipe); + getNodesByRecipe().put(recipe, result); + if (getRecipeTraces().add(recipeTrace)) + result.getNodeCache().assignTraceInfo(recipeTrace); + // Bug 491922: ensure that recipe shadowing propagates to + // parent traces + // note that if equivalentRecipes() becomes more + // sophisticated + // and considers recipes with different parents, this might + // have to be changed + ensureParents(recipeTrace); + } else { + // CONSTRUCTION IN PROGRESS FOR EQUIVALENT RECIPE + if (recipe instanceof IndexerRecipe) { + // this is allowed for indexers; + // go on with the construction, as the same indexer node + // will be obtained anyways + } else { + throw new IllegalStateException( + "This should not happen: " + "non-indexer nodes are are supposed to be constructed " + + "as soon as they are designated as canonical recipes"); + } + } + } + if (result == null) { + // MUST INSTANTIATE NEW NODE FOR RECIPE + final Node freshNode = instantiateNodeForRecipe(recipeTrace, recipe); + result = reteContainer.makeAddress(freshNode); + } + } + return result; + } + + private Set getRecipeTraces() { + return reteContainer.network.recipeTraces; + } + + private Node instantiateNodeForRecipe(RecipeTraceInfo recipeTrace, final ReteNodeRecipe recipe) { + this.getRecipeTraces().add(recipeTrace); + if (recipe instanceof IndexerRecipe) { + + // INSTANTIATE AND HOOK UP + // (cannot delay hooking up, because parent determines indexer + // implementation) + ensureParents(recipeTrace); + final ReteNodeRecipe parentRecipe = recipeTrace.getParentRecipeTraces().iterator().next().getRecipe(); + final Indexer result = nodeFactory.createIndexer(reteContainer, (IndexerRecipe) recipe, + asSupplier( + (Address) reteContainer.network.getExistingNodeByRecipe(parentRecipe)), + recipeTrace); + + // REMEMBER + if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) { + getNodesByRecipe().put(recipe, reteContainer.makeAddress(result)); + } + + return result; + } else { + + // INSTANTIATE + Node result = nodeFactory.createNode(reteContainer, recipe, recipeTrace); + + // REMEMBER + if (Options.nodeSharingOption == Options.NodeSharingOption.ALL) { + getNodesByRecipe().put(recipe, reteContainer.makeAddress(result)); + } + + // HOOK UP + // (recursion-tolerant due to this delayed order of initialization) + if (recipe instanceof InputRecipe) { + inputConnector.connectInput((InputRecipe) recipe, result); + } else { + if (recipe instanceof InputFilterRecipe) + inputConnector.connectInputFilter((InputFilterRecipe) recipe, result); + ensureParents(recipeTrace); + connectionFactory.connectToParents(recipeTrace, result); + } + return result; + } + } + + private Map> getNodesByRecipe() { + return reteContainer.network.nodesByRecipe; + } + + private void ensureParents(RecipeTraceInfo recipeTrace) { + for (RecipeTraceInfo parentTrace : recipeTrace.getParentRecipeTraces()) { + getOrCreateNodeByRecipe(parentTrace); + } + } + + //// Remoting - TODO eliminate? + + synchronized RemoteReceiver accessRemoteReceiver(Address address) { + throw new UnsupportedOperationException("Multi-container Rete not supported yet"); + // if (!reteContainer.isLocal(address)) + // return + // address.getContainer().getProvisioner().accessRemoteReceiver(address); + // Supplier localSupplier = reteContainer.resolveLocal(address); + // RemoteReceiver result = remoteReceivers.get(localSupplier); + // if (result == null) { + // result = new RemoteReceiver(reteContainer); + // reteContainer.connect(localSupplier, result); // stateless node, no + // // synch required + // + // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) + // remoteReceivers.put(localSupplier, result); + // } + // return result; + } + + /** + * @pre: address is NOT local + */ + synchronized RemoteSupplier accessRemoteSupplier(Address address) { + throw new UnsupportedOperationException("Multi-container Rete not supported yet"); + // RemoteSupplier result = remoteSuppliers.get(address); + // if (result == null) { + // result = new RemoteSupplier(reteContainer, + // address.getContainer().getProvisioner() + // .accessRemoteReceiver(address)); + // // network.connectAndSynchronize(supplier, result); + // + // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) + // remoteSuppliers.put(address, result); + // } + // return result; + } + + /** + * The powerful method for accessing any (supplier) Address as a local supplier. + */ + public Supplier asSupplier(Address address) { + if (!reteContainer.isLocal(address)) + return accessRemoteSupplier(address); + else + return reteContainer.resolveLocal(address); + } + + /** the composite key tuple is formed as (RecipeTraceInfo, TupleMask) */ + private Map projectionIndexerUserRequests = CollectionsFactory.createMap(); + + // local version + // TODO remove? + public synchronized ProjectionIndexer accessProjectionIndexer(RecipeTraceInfo productionTrace, TupleMask mask) { + Tuple tableKey = Tuples.staticArityFlatTupleOf(productionTrace, mask); + UserRequestTrace indexerTrace = projectionIndexerUserRequests.computeIfAbsent(tableKey, k -> { + final ProjectionIndexerRecipe projectionIndexerRecipe = projectionIndexerRecipe( + productionTrace, mask); + return new UserRequestTrace(projectionIndexerRecipe, productionTrace); + }); + final Address address = getOrCreateNodeByRecipe(indexerTrace); + return (ProjectionIndexer) reteContainer.resolveLocal(address); + } + + // local version + public synchronized ProjectionIndexer accessProjectionIndexerOnetime(RecipeTraceInfo supplierTrace, + TupleMask mask) { + if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) + return accessProjectionIndexer(supplierTrace, mask); + + final Address supplierAddress = getOrCreateNodeByRecipe(supplierTrace); + Supplier supplier = (Supplier) reteContainer.resolveLocal(supplierAddress); + + OnetimeIndexer result = new OnetimeIndexer(reteContainer, mask); + reteContainer.getDelayedCommandQueue().add(new DelayedConnectCommand(supplier, result, reteContainer)); + + return result; + } + + // local, read-only version + public synchronized ProjectionIndexer peekProjectionIndexer(RecipeTraceInfo supplierTrace, TupleMask mask) { + final Address address = getNodesByRecipe().get(projectionIndexerRecipe(supplierTrace, mask)); + return address == null ? null : (ProjectionIndexer) reteContainer.resolveLocal(address); + } + + private ProjectionIndexerRecipe projectionIndexerRecipe( + RecipeTraceInfo parentTrace, TupleMask mask) { + final ReteNodeRecipe parentRecipe = parentTrace.getRecipe(); + Tuple tableKey = Tuples.staticArityFlatTupleOf(parentRecipe, mask); + ProjectionIndexerRecipe projectionIndexerRecipe = resultSeedRecipes.computeIfAbsent(tableKey, k -> + RecipesHelper.projectionIndexerRecipe(parentRecipe, CompilerHelper.toRecipeMask(mask)) + ); + return projectionIndexerRecipe; + } + + /** the composite key tuple is formed as (ReteNodeRecipe, TupleMask) */ + private Map resultSeedRecipes = CollectionsFactory.createMap(); + + // public synchronized Address + // accessValueBinderFilterNode( + // Address supplierAddress, int bindingIndex, Object + // bindingValue) { + // Supplier supplier = asSupplier(supplierAddress); + // Object[] paramsArray = { supplier.getNodeId(), bindingIndex, bindingValue + // }; + // Tuple params = new FlatTuple(paramsArray); + // ValueBinderFilterNode result = valueBinderFilters.get(params); + // if (result == null) { + // result = new ValueBinderFilterNode(reteContainer, bindingIndex, + // bindingValue); + // reteContainer.connect(supplier, result); // stateless node, no synch + // // required + // + // if (Options.nodeSharingOption == Options.NodeSharingOption.ALL) + // valueBinderFilters.put(params, result); + // } + // return reteContainer.makeAddress(result); + // } + + /** + * Returns a copy of the given indexer that is an active node by itself (created if does not exist). (Convention: + * attached with same mask to a transparent node that is attached to parent node.) Node is created if it does not + * exist yet. + * + * @return an identical but active indexer + */ + // TODO rethink traceability + RecipeTraceInfo accessActiveIndexer(RecipeTraceInfo inactiveIndexerRecipeTrace) { + final RecipeTraceInfo parentRecipeTrace = inactiveIndexerRecipeTrace.getParentRecipeTraces().iterator().next(); + final ProjectionIndexerRecipe inactiveIndexerRecipe = (ProjectionIndexerRecipe) inactiveIndexerRecipeTrace + .getRecipe(); + + final TransparentRecipe transparentRecipe = RecipesFactory.eINSTANCE.createTransparentRecipe(); + transparentRecipe.setParent(parentRecipeTrace.getRecipe()); + final ActiveNodeConflictTrace transparentRecipeTrace = new ActiveNodeConflictTrace(transparentRecipe, + parentRecipeTrace, inactiveIndexerRecipeTrace); + + final ProjectionIndexerRecipe activeIndexerRecipe = RecipesFactory.eINSTANCE + .createProjectionIndexerRecipe(); + activeIndexerRecipe.setParent(transparentRecipe); + activeIndexerRecipe.setMask(inactiveIndexerRecipe.getMask()); + final ActiveNodeConflictTrace activeIndexerRecipeTrace = new ActiveNodeConflictTrace(activeIndexerRecipe, + transparentRecipeTrace, inactiveIndexerRecipeTrace); + + return activeIndexerRecipeTrace; + } + + // /** + // * @param parent + // * @return + // */ + // private TransparentNode accessTransparentNodeInternal(Supplier parent) { + // nodeFactory. + // return null; + // } + + // public synchronized void registerSpecializedProjectionIndexer(Node node, + // ProjectionIndexer indexer) { + // if (Options.nodeSharingOption != Options.NodeSharingOption.NEVER) { + // Object[] paramsArray = { node.getNodeId(), indexer.getMask() }; + // Tuple params = new FlatTuple(paramsArray); + // projectionIndexers.put(params, indexer); + // } + // } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java new file mode 100644 index 00000000..1eaa18e7 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/PosetAwareReceiver.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network; + +import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Direction; + +/** + * @author Tamas Szabo + * @since 2.0 + */ +public interface PosetAwareReceiver extends Receiver { + + public TupleMask getCoreMask(); + + public TupleMask getPosetMask(); + + public IPosetComparator getPosetComparator(); + + /** + * Updates the receiver with a newly found or lost partial matching also providing information + * whether the update is a monotone change or not. + * + * @param direction the direction of the update + * @param update the update tuple + * @param monotone true if the update is monotone, false otherwise + * @since 2.4 + */ + public void updateWithPosetInfo(Direction direction, Tuple update, boolean monotone); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java new file mode 100644 index 00000000..211194c0 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ProductionNode.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.network; + +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * Interface intended for nodes containing complete matches. + * + * @author Gabor Bergmann + */ +public interface ProductionNode extends Tunnel, Iterable { + + /** + * @return the position mapping of this particular pattern that maps members of the tuple type to their positions + */ + Map getPosMapping(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java new file mode 100644 index 00000000..3dc9aad7 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Receiver.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.network; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +/** + * ALL METHODS: FOR INTERNAL USE ONLY; ONLY INVOKE FROM {@link ReteContainer} + * + * @author Gabor Bergmann + * @noimplement This interface is not intended to be implemented by external clients. + */ +public interface Receiver extends Node { + + /** + * Updates the receiver with a newly found or lost partial matching. + * + * @since 2.4 + */ + public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp); + + /** + * Updates the receiver in batch style with a collection of updates. The input collection consists of pairs in the + * form (t, c) where t is an update tuple and c is the count. The count can also be negative, and it specifies how + * many times the tuple t gets deleted or inserted. The default implementation of this method simply calls + * {@link #update(Direction, Tuple, Timestamp)} individually for all updates. + * + * @since 2.8 + */ + public default void batchUpdate(final Collection> updates, final Timestamp timestamp) { + for (final Entry entry : updates) { + int count = entry.getValue(); + + Direction direction; + if (count < 0) { + direction = Direction.DELETE; + count = -count; + } else { + direction = Direction.INSERT; + } + + for (int i = 0; i < count; i++) { + update(direction, entry.getKey(), timestamp); + } + } + } + + /** + * Returns the {@link Mailbox} of this receiver. + * + * @return the mailbox + * @since 2.0 + */ + public Mailbox getMailbox(); + + /** + * appends a parent that will continuously send insert and revoke updates to this supplier + */ + void appendParent(final Supplier supplier); + + /** + * removes a parent + */ + void removeParent(final Supplier supplier); + + /** + * access active parent + */ + Collection getParents(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java new file mode 100644 index 00000000..cae78d37 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/RederivableNode.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network; + +/** + * A rederivable node can potentially re-derive tuples after the Rete network has finished the delivery of messages. + * + * @author Tamas Szabo + * @since 1.6 + */ +public interface RederivableNode extends Node, IGroupable { + + /** + * The method is called by the {@link ReteContainer} to re-derive tuples after the normal messages have been + * delivered and consumed. The re-derivation process may trigger the creation and delivery of further messages + * and further re-derivation rounds. + */ + public void rederiveOne(); + + /** + * Returns true if this node actually runs in DRed mode (not necessarily). + * + * @return true if the node is operating in DRed mode + * @since 2.0 + */ + public boolean isInDRedMode(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java new file mode 100644 index 00000000..09bff29e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReinitializedNode.java @@ -0,0 +1,14 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.rete.network; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +import java.util.Collection; + +public interface ReinitializedNode { + void reinitializeWith(Collection tuples); +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java new file mode 100644 index 00000000..16e290fd --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java @@ -0,0 +1,729 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.network; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Deque; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +import org.apache.log4j.Logger; +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Clearable; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.boundary.InputConnector; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timeless.TimelessCommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyCommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.delayed.DelayedCommand; +import tools.refinery.viatra.runtime.rete.network.delayed.DelayedConnectCommand; +import tools.refinery.viatra.runtime.rete.network.delayed.DelayedDisconnectCommand; +import tools.refinery.viatra.runtime.rete.remote.Address; +import tools.refinery.viatra.runtime.rete.single.SingleInputNode; +import tools.refinery.viatra.runtime.rete.single.TrimmerNode; +import tools.refinery.viatra.runtime.rete.util.Options; + +/** + * @author Gabor Bergmann + * + * Mutexes: externalMessageLock - enlisting messages into and retrieving from the external message queue + * @since 2.2 + */ +public final class ReteContainer { + + protected Thread consumerThread = null; + protected boolean killed = false; + + protected Network network; + + protected LinkedList clearables; + protected Map nodesById; + protected long nextId = 0; + + protected ConnectionFactory connectionFactory; + protected NodeProvisioner nodeProvisioner; + + protected Deque internalMessageQueue = new ArrayDeque(); + protected/* volatile */Deque externalMessageQueue = new ArrayDeque(); + protected Object externalMessageLock = new Object(); + protected Long clock = 1L; // even: steady state, odd: active queue; access + // ONLY with messageQueue locked! + protected Map terminationCriteria = null; + protected final Logger logger; + protected final CommunicationTracker tracker; + + protected final IQueryBackendContext backendContext; + + protected Set delayedCommandQueue; + protected Set delayedCommandBuffer; + protected boolean executingDelayedCommands; + + protected final TimelyConfiguration timelyConfiguration; + + /** + * @param threaded + * false if operating in a single-threaded environment + */ + public ReteContainer(Network network, boolean threaded) { + super(); + this.network = network; + this.backendContext = network.getEngine().getBackendContext(); + this.timelyConfiguration = network.getEngine().getTimelyConfiguration(); + + this.delayedCommandQueue = new LinkedHashSet(); + this.delayedCommandBuffer = new LinkedHashSet(); + this.executingDelayedCommands = false; + + if (this.isTimelyEvaluation()) { + this.tracker = new TimelyCommunicationTracker(this.getTimelyConfiguration()); + } else { + this.tracker = new TimelessCommunicationTracker(); + } + + this.nodesById = CollectionsFactory.createMap(); + this.clearables = new LinkedList(); + this.logger = network.getEngine().getLogger(); + + this.connectionFactory = new ConnectionFactory(this); + this.nodeProvisioner = new NodeProvisioner(this); + + if (threaded) { + this.terminationCriteria = CollectionsFactory.createMap(); + this.consumerThread = new Thread("Rete thread of " + ReteContainer.super.toString()) { + @Override + public void run() { + messageConsumptionCycle(); + } + }; + this.consumerThread.start(); + } + } + + /** + * @since 2.4 + */ + public boolean isTimelyEvaluation() { + return this.timelyConfiguration != null; + } + + /** + * @since 2.4 + */ + public TimelyConfiguration getTimelyConfiguration() { + return this.timelyConfiguration; + } + + /** + * @since 1.6 + * @return the communication graph of the nodes, incl. message scheduling + */ + public CommunicationTracker getCommunicationTracker() { + return tracker; + } + + /** + * Stops this container. To be called by Network.kill() + */ + public void kill() { + killed = true; + if (consumerThread != null) + consumerThread.interrupt(); + } + + /** + * Establishes connection between a supplier and a receiver node, regardless which container they are in. Assumption + * is that this container is the home of the receiver, but it is not strictly necessary. + * + * @param synchronise + * indicates whether the receiver should be synchronised to the current contents of the supplier + */ + public void connectRemoteNodes(Address supplier, Address receiver, + boolean synchronise) { + if (!isLocal(receiver)) + receiver.getContainer().connectRemoteNodes(supplier, receiver, synchronise); + else { + Receiver child = resolveLocal(receiver); + connectRemoteSupplier(supplier, child, synchronise); + } + } + + /** + * Severs connection between a supplier and a receiver node, regardless which container they are in. Assumption is + * that this container is the home of the receiver, but it is not strictly necessary. + * + * @param desynchronise + * indicates whether the current contents of the supplier should be subtracted from the receiver + */ + public void disconnectRemoteNodes(Address supplier, Address receiver, + boolean desynchronise) { + if (!isLocal(receiver)) + receiver.getContainer().disconnectRemoteNodes(supplier, receiver, desynchronise); + else { + Receiver child = resolveLocal(receiver); + disconnectRemoteSupplier(supplier, child, desynchronise); + } + } + + /** + * Establishes connection between a remote supplier and a local receiver node. + * + * @param synchronise + * indicates whether the receiver should be synchronised to the current contents of the supplier + */ + public void connectRemoteSupplier(Address supplier, Receiver receiver, boolean synchronise) { + Supplier parent = nodeProvisioner.asSupplier(supplier); + if (synchronise) + connectAndSynchronize(parent, receiver); + else + connect(parent, receiver); + } + + /** + * Severs connection between a remote supplier and a local receiver node. + * + * @param desynchronise + * indicates whether the current contents of the supplier should be subtracted from the receiver + */ + public void disconnectRemoteSupplier(Address supplier, Receiver receiver, + boolean desynchronise) { + Supplier parent = nodeProvisioner.asSupplier(supplier); + if (desynchronise) + disconnectAndDesynchronize(parent, receiver); + else + disconnect(parent, receiver); + } + + /** + * Connects a receiver to a supplier + */ + public void connect(Supplier supplier, Receiver receiver) { + supplier.appendChild(receiver); + receiver.appendParent(supplier); + tracker.registerDependency(supplier, receiver); + } + + /** + * Disconnects a receiver from a supplier + */ + public void disconnect(Supplier supplier, Receiver receiver) { + supplier.removeChild(receiver); + receiver.removeParent(supplier); + tracker.unregisterDependency(supplier, receiver); + } + + /** + * @since 2.3 + */ + public boolean isExecutingDelayedCommands() { + return this.executingDelayedCommands; + } + + /** + * @since 2.3 + */ + public Set getDelayedCommandQueue() { + if (this.executingDelayedCommands) { + return this.delayedCommandBuffer; + } else { + return this.delayedCommandQueue; + } + } + + /** + * Connects a receiver to a remote supplier, and synchronizes it to the current contents of the supplier + */ + public void connectAndSynchronize(Supplier supplier, Receiver receiver) { + supplier.appendChild(receiver); + receiver.appendParent(supplier); + tracker.registerDependency(supplier, receiver); + getDelayedCommandQueue().add(new DelayedConnectCommand(supplier, receiver, this)); + } + + /** + * Disconnects a receiver from a supplier + */ + public void disconnectAndDesynchronize(Supplier supplier, Receiver receiver) { + final boolean wasInSameSCC = this.isTimelyEvaluation() && this.tracker.areInSameGroup(supplier, receiver); + supplier.removeChild(receiver); + receiver.removeParent(supplier); + tracker.unregisterDependency(supplier, receiver); + getDelayedCommandQueue().add(new DelayedDisconnectCommand(supplier, receiver, this, wasInSameSCC)); + } + + /** + * @since 2.3 + */ + public void executeDelayedCommands() { + if (!this.delayedCommandQueue.isEmpty()) { + flushUpdates(); + this.executingDelayedCommands = true; + for (final DelayedCommand command : this.delayedCommandQueue) { + command.run(); + } + this.delayedCommandQueue = this.delayedCommandBuffer; + this.delayedCommandBuffer = new LinkedHashSet(); + flushUpdates(); + this.executingDelayedCommands = false; + } + } + + /** + * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The receiver is + * indicated by the Address. Designed to be called by the Network, DO NOT use in any other way. @pre: + * address.container == this, e.g. address MUST be local + * + * @return the value of the container's clock at the time when the message was accepted into the local message queue + */ + long sendUpdateToLocalAddress(Address address, Direction direction, Tuple updateElement) { + long timestamp; + Receiver receiver = resolveLocal(address); + UpdateMessage message = new UpdateMessage(receiver, direction, updateElement); + synchronized (externalMessageLock) { + externalMessageQueue.add(message); + timestamp = clock; + externalMessageLock.notifyAll(); + } + + return timestamp; + + } + + /** + * Sends multiple update messages atomically to the receiver node, indicating a newly found or lost partial + * matching. The receiver is indicated by the Address. Designed to be called by the Network, DO NOT use in any other + * way. @pre: address.container == this, e.g. address MUST be local @pre: updateElements is nonempty! + * + * @return the value of the container's clock at the time when the message was accepted into the local message queue + */ + long sendUpdatesToLocalAddress(Address address, Direction direction, + Collection updateElements) { + + long timestamp; + Receiver receiver = resolveLocal(address); + // UpdateMessage message = new UpdateMessage(receiver, direction, + // updateElement); + synchronized (externalMessageLock) { + for (Tuple ps : updateElements) + externalMessageQueue.add(new UpdateMessage(receiver, direction, ps)); + // messageQueue.add(new UpdateMessage(resolveLocal(address), + // direction, updateElement)); + // this.sendUpdateInternal(resolveLocal(address), direction, + // updateElement); + timestamp = clock; + externalMessageLock.notifyAll(); + } + + return timestamp; + } + + /** + * Sends an update message to the receiver node, indicating a newly found or lost partial matching. The receiver is + * indicated by the Address. Designed to be called by the Network in single-threaded operation, DO NOT use in any + * other way. + */ + void sendUpdateToLocalAddressSingleThreaded(Address address, Direction direction, + Tuple updateElement) { + Receiver receiver = resolveLocal(address); + UpdateMessage message = new UpdateMessage(receiver, direction, updateElement); + internalMessageQueue.add(message); + } + + /** + * Sends multiple update messages to the receiver node, indicating a newly found or lost partial matching. The + * receiver is indicated by the Address. Designed to be called by the Network in single-threaded operation, DO NOT + * use in any other way. + * + * @pre: address.container == this, e.g. address MUST be local + */ + void sendUpdatesToLocalAddressSingleThreaded(Address address, Direction direction, + Collection updateElements) { + Receiver receiver = resolveLocal(address); + for (Tuple ps : updateElements) + internalMessageQueue.add(new UpdateMessage(receiver, direction, ps)); + } + + /** + * Sends an update message to a node in a different container. The receiver is indicated by the Address. Designed to + * be called by RemoteReceivers, DO NOT use in any other way. + * + * @since 2.4 + */ + public void sendUpdateToRemoteAddress(Address address, Direction direction, + Tuple updateElement) { + ReteContainer otherContainer = address.getContainer(); + long otherClock = otherContainer.sendUpdateToLocalAddress(address, direction, updateElement); + // Long criterion = terminationCriteria.get(otherContainer); + // if (criterion==null || otherClock > criterion) + terminationCriteria.put(otherContainer, otherClock); + } + + /** + * Finalises all update sequences and returns. To be called from user threads (e.g. network construction). + */ + public void flushUpdates() { + network.waitForReteTermination(); + // synchronized (messageQueue) + // { + // while (!messageQueue.isEmpty()) + // { + // try { + // UpdateMessage message = messageQueue.take(); + // message.receiver.update(message.direction, message.updateElement); + // } catch (InterruptedException e) {} + // } + // } + } + + /** + * Retrieves a safe copy of the contents of a supplier. + * + *

Note that there may be multiple copies of a Tuple in case of a {@link TrimmerNode}, so the result is not always a set. + * + * @param flush if true, a flush is performed before pulling the contents + * @since 2.3 + */ + public Collection pullContents(final Supplier supplier, final boolean flush) { + if (flush) { + flushUpdates(); + } + final Collection collector = new ArrayList(); + supplier.pullInto(collector, flush); + return collector; + } + + /** + * @since 2.4 + */ + public Map> pullContentsWithTimeline(final Supplier supplier, final boolean flush) { + if (flush) { + flushUpdates(); + } + final Map> collector = CollectionsFactory.createMap(); + supplier.pullIntoWithTimeline(collector, flush); + return collector; + } + + /** + * Retrieves the contents of a SingleInputNode's parentage. + * + * @since 2.3 + */ + public Collection pullPropagatedContents(final SingleInputNode supplier, final boolean flush) { + if (flush) { + flushUpdates(); + } + final Collection collector = new LinkedList(); + supplier.propagatePullInto(collector, flush); + return collector; + } + + /** + * Retrieves the timestamp-aware contents of a SingleInputNode's parentage. + * + * @since 2.3 + */ + public Map> pullPropagatedContentsWithTimestamp(final SingleInputNode supplier, + final boolean flush) { + if (flush) { + flushUpdates(); + } + final Map> collector = CollectionsFactory.createMap(); + supplier.propagatePullIntoWithTimestamp(collector, flush); + return collector; + } + + /** + * Retrieves the contents of a supplier for a remote caller. Assumption is that this container is the home of the + * supplier, but it is not strictly necessary. + * + * @param supplier + * the address of the supplier to be pulled. + * @since 2.3 + */ + public Collection remotePull(Address supplier, boolean flush) { + if (!isLocal(supplier)) + return supplier.getContainer().remotePull(supplier, flush); + return pullContents(resolveLocal(supplier), flush); + } + + /** + * Proxies for the getPosMapping() of Production nodes. Retrieves the posmapping of a remote or local Production to + * a remote or local caller. + */ + public Map remotePosMapping(Address production) { + if (!isLocal(production)) + return production.getContainer().remotePosMapping(production); + return resolveLocal(production).getPosMapping(); + } + + /** + * Continually consumes update messages. Should be run on a dedicated thread. + */ + void messageConsumptionCycle() { + while (!killed) // deliver messages on and on and on.... + { + long incrementedClock = 0; + UpdateMessage message = null; + + if (!internalMessageQueue.isEmpty()) // take internal messages first + message = internalMessageQueue.removeFirst(); + else + // no internal message, take an incoming message + synchronized (externalMessageLock) { // no sleeping allowed, + // because external + // queue is locked for + // precise clocking of + // termination point! + if (!externalMessageQueue.isEmpty()) { // if external queue + // is non-empty, + // retrieve the next + // message instantly + message = takeExternalMessage(); + } else { // if external queue is found empty (and this is + // the first time in a row) + incrementedClock = ++clock; // local termination point + // synchronized(clock){incrementedClock = ++clock;} + } + } + + if (message == null) // both queues were empty + { + localUpdateTermination(incrementedClock); // report local + // termination point + while (message == null) // wait for a message while external + // queue is still empty + { + synchronized (externalMessageLock) { + while (externalMessageQueue.isEmpty()) { + try { + externalMessageLock.wait(); + } catch (InterruptedException e) { + if (killed) + return; + } + } + message = takeExternalMessage(); + } + + } + } + + // now we have a message to deliver + // NOTE: this method is not compatible with differential dataflow + message.receiver.update(message.direction, message.updateElement, Timestamp.ZERO); + } + } + + /** + * @since 1.6 + */ + public static final Function NAME_MAPPER = input -> input.toString().substring(0, + Math.min(30, input.toString().length())); + + /** + * Sends out all pending messages to their receivers. The delivery is governed by the communication tracker. + * + * @since 1.6 + */ + public void deliverMessagesSingleThreaded() { + if (!backendContext.areUpdatesDelayed()) { + if (Options.MONITOR_VIOLATION_OF_RETE_NODEGROUP_TOPOLOGICAL_SORTING) { + // known unreachable; enable for debugging only + + CommunicationGroup lastGroup = null; + Set seenInThisCycle = new HashSet<>(); + + while (!tracker.isEmpty()) { + final CommunicationGroup group = tracker.getAndRemoveFirstGroup(); + + /** + * The current group does not violate the communication schema iff (1) it was not seen before OR (2) + * the last one that was seen is exactly the same as the current one this can happen if the group + * was added back because of in-group message passing + */ + boolean okGroup = (group == lastGroup) || seenInThisCycle.add(group); + + if (!okGroup) { + logger.error( + "[INTERNAL ERROR] Violation of communication schema! The communication component with representative " + + group.getRepresentative() + " has already been processed!"); + } + + group.deliverMessages(); + + lastGroup = group; + } + + } else { + while (!tracker.isEmpty()) { + final CommunicationGroup group = tracker.getAndRemoveFirstGroup(); + group.deliverMessages(); + } + } + } + } + + private void localUpdateTermination(long incrementedClock) { + network.reportLocalUpdateTermination(this, incrementedClock, terminationCriteria); + terminationCriteria.clear(); + + // synchronized(clock){++clock;} // +1 incrementing for parity and easy + // comparison + } + + // @pre: externalMessageQueue synchronized && nonempty + private UpdateMessage takeExternalMessage() { + UpdateMessage message = externalMessageQueue.removeFirst(); + if (!externalMessageQueue.isEmpty()) { // copy the whole queue over + // for speedup + Deque temp = externalMessageQueue; + externalMessageQueue = internalMessageQueue; + internalMessageQueue = temp; + } + return message; + } + + /** + * Provides an external address for the selected node. + * + * @pre node belongs to this container. + */ + public Address makeAddress(N node) { + return new Address(node); + } + + /** + * Checks whether a certain address points to a node at this container. + */ + public boolean isLocal(Address address) { + return address.getContainer() == this; + } + + /** + * Returns an addressed node at this container. + * + * @pre: address.container == this, e.g. address MUST be local + * @throws IllegalArgumentException + * if address is non-local + */ + @SuppressWarnings("unchecked") + public N resolveLocal(Address address) { + if (this != address.getContainer()) + throw new IllegalArgumentException(String.format("Address %s non-local at container %s", address, this)); + + N cached = address.getNodeCache(); + if (cached != null) + return cached; + else { + N node = (N) nodesById.get(address.getNodeId()); + address.setNodeCache(node); + return node; + } + } + + /** + * Registers a node into the rete network (should be called by constructor). Every node MUST be registered by its + * constructor. + * + * @return the unique nodeId issued to the node. + */ + public long registerNode(Node n) { + long id = nextId++; + nodesById.put(id, n); + return id; + } + + /** + * Unregisters a node from the rete network. Do NOT call if node is still connected to other Nodes, or Adressed or + * otherwise referenced. + */ + public void unregisterNode(Node n) { + nodesById.remove(n.getNodeId()); + } + + /** + * Registers a pattern memory into the rete network. Every memory MUST be registered by its owner node. + */ + public void registerClearable(Clearable c) { + clearables.addFirst(c); + } + + /** + * Unregisters a pattern memory from the rete network. + */ + public void unregisterClearable(Clearable c) { + clearables.remove(c); + } + + /** + * Clears all memory contents in the network. Reverts to initial state. + */ + public void clearAll() { + for (Clearable c : clearables) { + c.clear(); + } + } + + public NodeFactory getNodeFactory() { + return network.getNodeFactory(); + } + + public ConnectionFactory getConnectionFactory() { + return connectionFactory; + } + + public NodeProvisioner getProvisioner() { + return nodeProvisioner; + } + + public Network getNetwork() { + return network; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + String separator = System.getProperty("line.separator"); + sb.append(super.toString() + "[[[" + separator); + java.util.List keys = new java.util.ArrayList(nodesById.keySet()); + java.util.Collections.sort(keys); + for (Long key : keys) { + sb.append(key + " -> " + nodesById.get(key) + separator); + } + sb.append("]]] of " + network); + return sb.toString(); + } + + /** + * Access all the Rete nodes inside this container. + * + * @return the collection of {@link Node} instances + */ + public Collection getAllNodes() { + return nodesById.values(); + } + + public InputConnector getInputConnectionFactory() { + return network.getInputConnector(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java new file mode 100644 index 00000000..e7ec36dc --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.network; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.index.GenericProjectionIndexer; +import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; + +/** + * Base implementation for a supplier node. + * + * @author Gabor Bergmann + * + */ +public abstract class StandardNode extends BaseNode implements Supplier, NetworkStructureChangeSensitiveNode { + protected final List children = CollectionsFactory.createObserverList(); + /** + * @since 2.2 + */ + protected final List childMailboxes = CollectionsFactory.createObserverList(); + + public StandardNode(final ReteContainer reteContainer) { + super(reteContainer); + } + + /** + * @since 2.4 + */ + protected void propagateUpdate(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { + for (final Mailbox childMailbox : childMailboxes) { + childMailbox.postMessage(direction, updateElement, timestamp); + } + } + + @Override + public void appendChild(final Receiver receiver) { + children.add(receiver); + childMailboxes.add(this.getCommunicationTracker().proxifyMailbox(this, receiver.getMailbox())); + } + + @Override + public void removeChild(final Receiver receiver) { + children.remove(receiver); + Mailbox mailboxToRemove = null; + for (final Mailbox mailbox : childMailboxes) { + if (mailbox.getReceiver() == receiver) { + mailboxToRemove = mailbox; + break; + } + } + assert mailboxToRemove != null; + childMailboxes.remove(mailboxToRemove); + } + + @Override + public void networkStructureChanged() { + childMailboxes.clear(); + for (final Receiver receiver : children) { + childMailboxes.add(this.getCommunicationTracker().proxifyMailbox(this, receiver.getMailbox())); + } + } + + @Override + public Collection getReceivers() { + return children; + } + + /** + * @since 2.2 + */ + public Collection getChildMailboxes() { + return this.childMailboxes; + } + + @Override + public Set getPulledContents(final boolean flush) { + final HashSet results = new HashSet(); + pullInto(results, flush); + return results; + } + + @Override + public ProjectionIndexer constructIndex(final TupleMask mask, final TraceInfo... traces) { + final GenericProjectionIndexer indexer = new GenericProjectionIndexer(reteContainer, mask); + for (final TraceInfo traceInfo : traces) { + indexer.assignTraceInfo(traceInfo); + } + reteContainer.connectAndSynchronize(this, indexer); + return indexer; + } + + /** + * @since 1.6 + */ + protected void issueError(final String message, final Exception ex) { + if (ex == null) { + this.reteContainer.getNetwork().getEngine().getLogger().error(message); + } else { + this.reteContainer.getNetwork().getEngine().getLogger().error(message, ex); + } + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java new file mode 100644 index 00000000..1917a7cf --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Supplier.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.network; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.single.TrimmerNode; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; + +/** + * @author Gabor Bergmann + * + * A supplier is an object that can propagate insert or revoke events towards receivers. + */ +public interface Supplier extends Node { + + /** + * Pulls the contents of this object in this particular moment into a target collection. + * + * @param flush if true, flushing of messages is allowed during the pull, otherwise flushing is not allowed + * @since 2.3 + */ + public void pullInto(Collection collector, boolean flush); + + /** + * @since 2.4 + */ + public void pullIntoWithTimeline(final Map> collector, final boolean flush); + + /** + * Returns the contents of this object in this particular moment. + * For memoryless nodes, this may involve a costly recomputation of contents. + * + * The result is returned as a Set, even when it has multiplicities (at the output of {@link TrimmerNode}). + * + *

Intended mainly for debug purposes; therefore flushing is performed only if explicitly requested + * During runtime, flushing may be preferred; see {@link ReteContainer#pullContents(Supplier)} + * @since 2.3 + */ + public Set getPulledContents(boolean flush); + + default public Set getPulledContents() { + return getPulledContents(true); + } + + /** + * appends a receiver that will continously receive insert and revoke updates from this supplier + */ + void appendChild(Receiver receiver); + + /** + * removes a receiver + */ + void removeChild(Receiver receiver); + + /** + * Instantiates (or reuses, depending on implementation) an index according to the given mask. + * + * Intended for internal use; clients should invoke through Library instead to enable reusing. + */ + ProjectionIndexer constructIndex(TupleMask mask, TraceInfo... traces); + + /** + * lists receivers + */ + Collection getReceivers(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java new file mode 100644 index 00000000..f238f47b --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/Tunnel.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.network; + +/** + * @author Gabor Bergmann + * + * A Tunnel is an interface into which elments can be instered and from which productions can be extracted. + */ +public interface Tunnel extends Supplier, Receiver { + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java new file mode 100644 index 00000000..1334a3a9 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/UpdateMessage.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.network; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; + +class UpdateMessage { + public Receiver receiver; + public Direction direction; + public Tuple updateElement; + + public UpdateMessage(Receiver receiver, Direction direction, Tuple updateElement) { + this.receiver = receiver; + this.direction = direction; + this.updateElement = updateElement; + } + + @Override + public String toString() { + return "M." + direction + ": " + updateElement + " -> " + receiver; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java new file mode 100644 index 00000000..8cedeb11 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationGroup.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication; + +import java.util.Collection; +import java.util.Map; + +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +/** + * A communication group represents a set of nodes in the communication graph that form a strongly connected component. + * + * @author Tamas Szabo + * @since 1.6 + */ +public abstract class CommunicationGroup implements Comparable { + + public static final String UNSUPPORTED_MESSAGE_KIND = "Unsupported message kind "; + + /** + * Marker for the {@link CommunicationTracker} + */ + public boolean isEnqueued = false; + + protected final Node representative; + + /** + * May be changed during bumping in {@link CommunicationTracker.registerDependency} + */ + protected int identifier; + + /** + * @since 1.7 + */ + protected final CommunicationTracker tracker; + + /** + * @since 1.7 + */ + public CommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) { + this.tracker = tracker; + this.representative = representative; + this.identifier = identifier; + } + + public abstract void deliverMessages(); + + public Node getRepresentative() { + return representative; + } + + public abstract boolean isEmpty(); + + /** + * @since 2.0 + */ + public abstract void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind); + + /** + * @since 2.0 + */ + public abstract void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind); + + public abstract Map> getMailboxes(); + + public abstract boolean isRecursive(); + + @Override + public int hashCode() { + return this.identifier; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + " " + this.identifier + " - representative: " + this.representative + + " - isEmpty: " + isEmpty(); + } + + @Override + public boolean equals(final Object obj) { + if (obj == null || this.getClass() != obj.getClass()) { + return false; + } else if (this == obj) { + return true; + } else { + final CommunicationGroup that = (CommunicationGroup) obj; + return this.identifier == that.identifier; + } + } + + @Override + public int compareTo(final CommunicationGroup that) { + return this.identifier - that.identifier; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java new file mode 100644 index 00000000..8435a547 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java @@ -0,0 +1,467 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.alg.incscc.IncSCCAlg; +import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting; +import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.rete.aggregation.IAggregatorNode; +import tools.refinery.viatra.runtime.rete.boundary.ExternalInputEnumeratorNode; +import tools.refinery.viatra.runtime.rete.eval.RelationEvaluatorNode; +import tools.refinery.viatra.runtime.rete.index.DualInputNode; +import tools.refinery.viatra.runtime.rete.index.ExistenceNode; +import tools.refinery.viatra.runtime.rete.index.Indexer; +import tools.refinery.viatra.runtime.rete.index.IndexerListener; +import tools.refinery.viatra.runtime.rete.index.IterableIndexer; +import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer; +import tools.refinery.viatra.runtime.rete.network.IGroupable; +import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.ProductionNode; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyIndexerListenerProxy; +import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyMailboxProxy; +import tools.refinery.viatra.runtime.rete.network.mailbox.FallThroughCapableMailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; +import tools.refinery.viatra.runtime.rete.single.TransitiveClosureNode; +import tools.refinery.viatra.runtime.rete.single.TrimmerNode; + +/** + * An instance of this class is associated with every {@link ReteContainer}. The tracker serves two purposes:
+ * (1) It allows RETE nodes to register their communication dependencies on-the-fly. These dependencies can be + * registered or unregistered when nodes are disposed of.
+ * (2) It allows RETE nodes to register their mailboxes as dirty, that is, they can tell the tracker that they have + * something to send to other nodes in the network. The tracker is then responsible for ordering these messages (more + * precisely, the mailboxes that contain the messages) for the associated {@link ReteContainer}. The ordering is + * governed by the strongly connected components in the dependency network and follows a topological sorting scheme; + * those mailboxes will be emptied first whose owner nodes do not depend on other undelivered messages. + * + * @author Tamas Szabo + * @since 1.6 + * + */ +public abstract class CommunicationTracker { + + /** + * The minimum group id assigned so far + */ + protected int minGroupId; + + /** + * The maximum group id assigned so far + */ + protected int maxGroupId; + + /** + * The dependency graph of the communications in the RETE network + */ + protected final Graph dependencyGraph; + + /** + * Incremental SCC information about the dependency graph + */ + protected final IncSCCAlg sccInformationProvider; + + /** + * Precomputed node -> communication group map + */ + protected final Map groupMap; + + /** + * Priority queue of active communication groups + */ + protected final Queue groupQueue; + + // groups should have a simple integer flag which represents its position in a priority queue + // priority queue only contains the ACTIVE groups + + public CommunicationTracker() { + this.dependencyGraph = new Graph(); + this.sccInformationProvider = new IncSCCAlg(this.dependencyGraph); + this.groupQueue = new PriorityQueue(); + this.groupMap = new HashMap(); + } + + public Graph getDependencyGraph() { + return dependencyGraph; + } + + public CommunicationGroup getGroup(final Node node) { + return this.groupMap.get(node); + } + + private void precomputeGroups() { + groupMap.clear(); + + // reconstruct group map from dependency graph + final Graph reducedGraph = sccInformationProvider.getReducedGraph(); + final List representatives = TopologicalSorting.compute(reducedGraph); + + for (int i = 0; i < representatives.size(); i++) { // groups for SCC representatives + final Node representative = representatives.get(i); + createAndStoreGroup(representative, i); + } + + minGroupId = 0; + maxGroupId = representatives.size() - 1; + + for (final Node node : dependencyGraph.getAllNodes()) { // extend group map to the rest of nodes + final Node representative = sccInformationProvider.getRepresentative(node); + final CommunicationGroup group = groupMap.get(representative); + if (representative != node) { + addToGroup(node, group); + } + } + + for (final Node node : dependencyGraph.getAllNodes()) { + // set fall-through flags of default mailboxes + precomputeFallThroughFlag(node); + // perform further tracker-specific post-processing + postProcessNode(node); + } + + // reconstruct new queue contents based on new group map + if (!groupQueue.isEmpty()) { + final Set oldActiveGroups = new HashSet(groupQueue); + groupQueue.clear(); + reconstructQueueContents(oldActiveGroups); + } + + // post process the groups + for (final CommunicationGroup group : groupMap.values()) { + postProcessGroup(group); + } + } + + /** + * This method is responsible for reconstructing the active queue contents after the network structure has changed. + * It it defined as abstract because the reconstruction logic is specific to each {@link CommunicationTracker}. + * @since 2.4 + */ + protected abstract void reconstructQueueContents(final Set oldActiveGroups); + + private void addToGroup(final Node node, final CommunicationGroup group) { + groupMap.put(node, group); + if (node instanceof Receiver) { + ((Receiver) node).getMailbox().setCurrentGroup(group); + if (node instanceof IGroupable) { + ((IGroupable) node).setCurrentGroup(group); + } + } + } + + /** + * Depends on the groups, as well as the parent nodes of the argument, so recomputation is needed if these change + */ + private void precomputeFallThroughFlag(final Node node) { + CommunicationGroup group = groupMap.get(node); + if (node instanceof Receiver) { + IGroupable mailbox = ((Receiver) node).getMailbox(); + if (mailbox instanceof FallThroughCapableMailbox) { + Set directParents = dependencyGraph.getSourceNodes(node).distinctValues(); + // decide between using quick&cheap fall-through, or allowing for update cancellation + boolean fallThrough = + // disallow fallthrough: updates at production nodes should cancel, if they can be trimmed or + // disjunctive + (!(node instanceof ProductionNode && ( // it is a production node... + // with more than one parent + directParents.size() > 0 || + // or true trimming in its sole parent + directParents.size() == 1 && trueTrimming(directParents.iterator().next())))) && + // disallow fallthrough: external updates should be stored (if updates are delayed) + (!(node instanceof ExternalInputEnumeratorNode)) && + // disallow fallthrough: RelationEvaluatorNode needs to be notified in batch-style, and the batching is done by the mailbox + // however, it is not the RelationEvaluatorNode itself that is interesting here, as that indirectly uses the BatchingReceiver + // so we need to disable fall-through for the BatchingReceiver + (!(node instanceof RelationEvaluatorNode.BatchingReceiver)); + // do additional checks + if (fallThrough) { + // recursive parent groups generate excess updates that should be cancelled after delete&rederive + // phases + // aggregator and transitive closure parent nodes also generate excess updates that should be + // cancelled + directParentLoop: for (Node directParent : directParents) { + Set parentsToCheck = new HashSet<>(); + // check the case where a direct parent is the reason for mailbox usage + parentsToCheck.add(directParent); + // check the case where an indirect parent (join slot) is the reason for mailbox usage + if (directParent instanceof DualInputNode) { + // in case of existence join (typically antijoin), a mailbox should allow + // an insertion and deletion (at the secondary slot) to cancel each other out + if (directParent instanceof ExistenceNode) { + fallThrough = false; + break directParentLoop; + } + // in beta nodes, indexer slots (or their active nodes) are considered indirect parents + DualInputNode dualInput = (DualInputNode) directParent; + IterableIndexer primarySlot = dualInput.getPrimarySlot(); + if (primarySlot != null) + parentsToCheck.add(primarySlot.getActiveNode()); + Indexer secondarySlot = dualInput.getSecondarySlot(); + if (secondarySlot != null) + parentsToCheck.add(secondarySlot.getActiveNode()); + } + for (Node parent : parentsToCheck) { + CommunicationGroup parentGroup = groupMap.get(parent); + if ( // parent is in a different, recursive group + (group != parentGroup && parentGroup.isRecursive()) || + // node and parent within the same recursive group, and... + (group == parentGroup && group.isRecursive() && ( + // parent is a transitive closure or aggregator node, or a trimmer + // allow trimmed or disjunctive tuple updates to cancel each other + (parent instanceof TransitiveClosureNode) || (parent instanceof IAggregatorNode) + || trueTrimming(parent)))) { + fallThrough = false; + break directParentLoop; + } + } + } + } + // overwrite fallthrough flag with newly computed value + ((FallThroughCapableMailbox) mailbox).setFallThrough(fallThrough); + } + } + } + + /** + * A trimmer node that actually eliminates some columns (not just reorders) + */ + private boolean trueTrimming(Node node) { + if (node instanceof TrimmerNode) { + TupleMask mask = ((TrimmerNode) node).getMask(); + return (mask.indices.length != mask.sourceWidth); + } + return false; + } + + public void activateUnenqueued(final CommunicationGroup group) { + groupQueue.add(group); + group.isEnqueued = true; + } + + public void deactivate(final CommunicationGroup group) { + groupQueue.remove(group); + group.isEnqueued = false; + } + + public CommunicationGroup getAndRemoveFirstGroup() { + final CommunicationGroup group = groupQueue.poll(); + group.isEnqueued = false; + return group; + } + + public boolean isEmpty() { + return groupQueue.isEmpty(); + } + + protected abstract CommunicationGroup createGroup(final Node representative, final int index); + + protected CommunicationGroup createAndStoreGroup(final Node representative, final int index) { + final CommunicationGroup group = createGroup(representative, index); + addToGroup(representative, group); + return group; + } + + /** + * Registers the dependency that the target {@link Node} depends on the source {@link Node}. In other words, source + * may send messages to target in the RETE network. If the dependency edge is already present, this method call is a + * noop. + * + * @param source + * the source node + * @param target + * the target node + */ + public void registerDependency(final Node source, final Node target) { + // nodes can be immediately inserted, if they already exist in the graph, this is a noop + dependencyGraph.insertNode(source); + dependencyGraph.insertNode(target); + + if (!this.dependencyGraph.getTargetNodes(source).containsNonZero(target)) { + + // query all these information before the actual edge insertion + // because SCCs may be unified during the process + final Node sourceRepresentative = sccInformationProvider.getRepresentative(source); + final Node targetRepresentative = sccInformationProvider.getRepresentative(target); + final boolean targetHadOutgoingEdges = sccInformationProvider.hasOutgoingEdges(targetRepresentative); + + // insert the edge + dependencyGraph.insertEdge(source, target); + + // create groups if they do not yet exist + CommunicationGroup sourceGroup = groupMap.get(sourceRepresentative); + if (sourceGroup == null) { + // create on-demand with the next smaller group id + sourceGroup = createAndStoreGroup(sourceRepresentative, --minGroupId); + } + final int sourceIndex = sourceGroup.identifier; + + CommunicationGroup targetGroup = groupMap.get(targetRepresentative); + if (targetGroup == null) { + // create on-demand with the next larger group id + targetGroup = createAndStoreGroup(targetRepresentative, ++maxGroupId); + } + final int targetIndex = targetGroup.identifier; + + if (sourceIndex <= targetIndex) { + // indices obey current topological ordering + refreshFallThroughFlag(target); + postProcessNode(source); + postProcessNode(target); + postProcessGroup(sourceGroup); + if (sourceGroup != targetGroup) { + postProcessGroup(targetGroup); + } + } else if (sourceIndex > targetIndex && !targetHadOutgoingEdges) { + // indices violate current topological ordering, but we can simply bump the target index + final boolean wasEnqueued = targetGroup.isEnqueued; + if (wasEnqueued) { + groupQueue.remove(targetGroup); + } + targetGroup.identifier = ++maxGroupId; + if (wasEnqueued) { + groupQueue.add(targetGroup); + } + + refreshFallThroughFlag(target); + postProcessNode(source); + postProcessNode(target); + postProcessGroup(sourceGroup); + postProcessGroup(targetGroup); + } else { + // needs a full re-computation because of more complex change + precomputeGroups(); + } + } + } + + /** + * Returns true if the given {@link Node} is in a recursive {@link CommunicationGroup}, false otherwise. + */ + public boolean isInRecursiveGroup(final Node node) { + final CommunicationGroup group = this.getGroup(node); + if (group == null) { + return false; + } else { + return group.isRecursive(); + } + } + + /** + * Returns true if the given two {@link Node}s are in the same {@link CommunicationGroup}. + */ + public boolean areInSameGroup(final Node left, final Node right) { + final CommunicationGroup leftGroup = this.getGroup(left); + final CommunicationGroup rightGroup = this.getGroup(right); + return leftGroup != null && leftGroup == rightGroup; + } + + /** + * Unregisters a dependency between source and target. + * + * @param source + * the source node + * @param target + * the target node + */ + public void unregisterDependency(final Node source, final Node target) { + // delete the edge first, and then query the SCC info provider + this.dependencyGraph.deleteEdgeIfExists(source, target); + + final Node sourceRepresentative = sccInformationProvider.getRepresentative(source); + final Node targetRepresentative = sccInformationProvider.getRepresentative(target); + + // if they are still in the same SCC, + // then this deletion did not affect the SCCs, + // and it is sufficient to recompute affected fall-through flags; + // otherwise, we need a new pre-computation for the groupMap and groupQueue + if (sourceRepresentative.equals(targetRepresentative)) { + // this deletion could not have affected the split flags + refreshFallThroughFlag(target); + postProcessNode(source); + postProcessNode(target); + } else { + // preComputeGroups takes care of the split flag maintenance + precomputeGroups(); + } + } + + /** + * Refresh fall-through flags if dependencies change for given target, but no SCC change + */ + private void refreshFallThroughFlag(final Node target) { + precomputeFallThroughFlag(target); + if (target instanceof DualInputNode) { + for (final Node indirectTarget : dependencyGraph.getTargetNodes(target).distinctValues()) { + precomputeFallThroughFlag(indirectTarget); + } + } + } + + /** + * Returns true if the given source-target edge in the communication network acts as a recursion cut point. + * The current implementation considers edges leading into {@link ProductionNode}s as cut point iff + * both source and target belong to the same group. + * + * @param source the source node + * @param target the target node + * @return true if the edge is a cut point, false otherwise + * @since 2.4 + */ + protected boolean isRecursionCutPoint(final Node source, final Node target) { + final Node effectiveSource = source instanceof SpecializedProjectionIndexer + ? ((SpecializedProjectionIndexer) source).getActiveNode() + : source; + final CommunicationGroup sourceGroup = this.getGroup(effectiveSource); + final CommunicationGroup targetGroup = this.getGroup(target); + return sourceGroup != null && sourceGroup == targetGroup && target instanceof ProductionNode; + } + + /** + * This hook allows concrete tracker implementations to perform tracker-specific post processing on nodes (cf. + * {@link NetworkStructureChangeSensitiveNode} and {@link BehaviorChangingMailbox}). At the time of the invocation, + * the network topology has already been updated. + */ + protected abstract void postProcessNode(final Node node); + + /** + * This hook allows concrete tracker implementations to perform tracker-specific post processing on groups. At the + * time of the invocation, the network topology has already been updated. + * @since 2.4 + */ + protected abstract void postProcessGroup(final CommunicationGroup group); + + /** + * Creates a proxy for the given {@link Mailbox} for the given requester {@link Node}. The proxy creation is + * {@link CommunicationTracker}-specific and depends on the identity of the requester. This method is primarily used + * to create {@link TimelyMailboxProxy}s depending on the network topology. There is no guarantee that the same + * proxy instance is returned when this method is called multiple times with the same arguments. + */ + public abstract Mailbox proxifyMailbox(final Node requester, final Mailbox original); + + /** + * Creates a proxy for the given {@link IndexerListener} for the given requester {@link Node}. The proxy creation is + * {@link CommunicationTracker}-specific and depends on the identity of the requester. This method is primarily used + * to create {@link TimelyIndexerListenerProxy}s depending on the network topology. There is no guarantee that the + * same proxy instance is returned when this method is called multiple times with the same arguments. + */ + public abstract IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java new file mode 100644 index 00000000..e1a61693 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/MessageSelector.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication; + +/** + * Subclasses of this interface represent meta data of update messages in Rete. + * + * @author Tamas Szabo + * @since 2.3 + */ +public interface MessageSelector { + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java new file mode 100644 index 00000000..27779352 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/NodeComparator.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication; + +import java.util.Comparator; +import java.util.Map; + +import tools.refinery.viatra.runtime.rete.network.Node; + +/** + * @since 2.4 + */ +public class NodeComparator implements Comparator { + + protected final Map nodeMap; + + public NodeComparator(final Map nodeMap) { + this.nodeMap = nodeMap; + } + + @Override + public int compare(final Node left, final Node right) { + return this.nodeMap.get(left) - this.nodeMap.get(right); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java new file mode 100644 index 00000000..41cd8cd3 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/PhasedSelector.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication; + +/** + * A default message selector that can be used to associate phases to messages. + * + * @author Tamas Szabo + * @since 2.3 + */ +public enum PhasedSelector implements MessageSelector { + + /** + * No special distinguishing feature + */ + DEFAULT, + + /** + * Inserts and delete-insert monotone change pairs + */ + MONOTONE, + + /** + * Deletes + */ + ANTI_MONOTONE + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java new file mode 100644 index 00000000..a50a63a8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/Timestamp.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication; + +import java.util.AbstractMap; +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; + +/** + * A timestamp associated with update messages in timely evaluation. + * + * @author Tamas Szabo + * @since 2.3 + */ +public class Timestamp implements Comparable, MessageSelector { + + protected final int value; + public static final Timestamp ZERO = new Timestamp(0); + /** + * @since 2.4 + */ + public static final Timeline INSERT_AT_ZERO_TIMELINE = Timelines.createFrom(Timestamp.ZERO); + + public Timestamp(final int value) { + this.value = value; + } + + public int getValue() { + return value; + } + + public Timestamp max(final Timestamp that) { + if (this.value >= that.value) { + return this; + } else { + return that; + } + } + + /** + * @since 2.4 + */ + public Timestamp min(final Timestamp that) { + if (this.value <= that.value) { + return this; + } else { + return that; + } + } + + @Override + public int compareTo(final Timestamp that) { + return this.value - that.value; + } + + @Override + public boolean equals(final Object obj) { + if (obj == null || !(obj instanceof Timestamp)) { + return false; + } else { + return this.value == ((Timestamp) obj).value; + } + } + + @Override + public int hashCode() { + return this.value; + } + + @Override + public String toString() { + return Integer.toString(this.value); + } + + /** + * A {@link Map} implementation that associates the zero timestamp with every key. There is no suppor for + * {@link Map#entrySet()} due to performance reasons. + * + * @author Tamas Szabo + */ + public static final class AllZeroMap extends AbstractMap> { + + private final Collection wrapped; + + public AllZeroMap(Set wrapped) { + this.wrapped = wrapped; + } + + @Override + public Set>> entrySet() { + throw new UnsupportedOperationException("Use the combination of keySet() and get()!"); + } + + /** + * @since 2.4 + */ + @Override + public Timeline get(final Object key) { + return INSERT_AT_ZERO_TIMELINE; + } + + @Override + public Set keySet() { + return (Set) this.wrapped; + } + + @Override + public String toString() { + return this.getClass().getSimpleName() + ": " + this.keySet().toString(); + } + + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java new file mode 100644 index 00000000..d8260384 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/RecursiveCommunicationGroup.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication.timeless; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.RederivableNode; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +/** + * A communication group representing either a single node where the + * node is a monotonicity aware one or a set of nodes that form an SCC. + * + * @author Tamas Szabo + * @since 2.4 + */ +public class RecursiveCommunicationGroup extends CommunicationGroup { + + private final Set antiMonotoneMailboxes; + private final Set monotoneMailboxes; + private final Set defaultMailboxes; + private final Set rederivables; + private boolean currentlyDelivering; + + /** + * @since 1.7 + */ + public RecursiveCommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) { + super(tracker, representative, identifier); + this.antiMonotoneMailboxes = CollectionsFactory.createSet(); + this.monotoneMailboxes = CollectionsFactory.createSet(); + this.defaultMailboxes = CollectionsFactory.createSet(); + this.rederivables = new LinkedHashSet(); + this.currentlyDelivering = false; + } + + @Override + public void deliverMessages() { + this.currentlyDelivering = true; + + // ANTI-MONOTONE PHASE + while (!this.antiMonotoneMailboxes.isEmpty() || !this.defaultMailboxes.isEmpty()) { + while (!this.antiMonotoneMailboxes.isEmpty()) { + final Mailbox mailbox = this.antiMonotoneMailboxes.iterator().next(); + this.antiMonotoneMailboxes.remove(mailbox); + mailbox.deliverAll(PhasedSelector.ANTI_MONOTONE); + } + while (!this.defaultMailboxes.isEmpty()) { + final Mailbox mailbox = this.defaultMailboxes.iterator().next(); + this.defaultMailboxes.remove(mailbox); + mailbox.deliverAll(PhasedSelector.DEFAULT); + } + } + + // REDERIVE PHASE + while (!this.rederivables.isEmpty()) { + // re-derivable nodes take care of their unregistration!! + final RederivableNode node = this.rederivables.iterator().next(); + node.rederiveOne(); + } + + // MONOTONE PHASE + while (!this.monotoneMailboxes.isEmpty() || !this.defaultMailboxes.isEmpty()) { + while (!this.monotoneMailboxes.isEmpty()) { + final Mailbox mailbox = this.monotoneMailboxes.iterator().next(); + this.monotoneMailboxes.remove(mailbox); + mailbox.deliverAll(PhasedSelector.MONOTONE); + } + while (!this.defaultMailboxes.isEmpty()) { + final Mailbox mailbox = this.defaultMailboxes.iterator().next(); + this.defaultMailboxes.remove(mailbox); + mailbox.deliverAll(PhasedSelector.DEFAULT); + } + } + + this.currentlyDelivering = false; + } + + @Override + public boolean isEmpty() { + return this.rederivables.isEmpty() && this.antiMonotoneMailboxes.isEmpty() + && this.monotoneMailboxes.isEmpty() && this.defaultMailboxes.isEmpty(); + } + + @Override + public void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind) { + final Collection mailboxes = getMailboxContainer(kind); + mailboxes.add(mailbox); + if (!this.isEnqueued && !this.currentlyDelivering) { + this.tracker.activateUnenqueued(this); + } + } + + @Override + public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) { + final Collection mailboxes = getMailboxContainer(kind); + mailboxes.remove(mailbox); + if (isEmpty()) { + this.tracker.deactivate(this); + } + } + + private Collection getMailboxContainer(final MessageSelector kind) { + if (kind == PhasedSelector.ANTI_MONOTONE) { + return this.antiMonotoneMailboxes; + } else if (kind == PhasedSelector.MONOTONE) { + return this.monotoneMailboxes; + } else if (kind == PhasedSelector.DEFAULT) { + return this.defaultMailboxes; + } else { + throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind); + } + } + + public void addRederivable(final RederivableNode node) { + this.rederivables.add(node); + if (!this.isEnqueued) { + this.tracker.activateUnenqueued(this); + } + } + + public void removeRederivable(final RederivableNode node) { + this.rederivables.remove(node); + if (isEmpty()) { + this.tracker.deactivate(this); + } + } + + public Collection getRederivables() { + return this.rederivables; + } + + @Override + public Map> getMailboxes() { + Map> map = new EnumMap<>(PhasedSelector.class); + map.put(PhasedSelector.ANTI_MONOTONE, antiMonotoneMailboxes); + map.put(PhasedSelector.MONOTONE, monotoneMailboxes); + map.put(PhasedSelector.DEFAULT, defaultMailboxes); + return Collections.unmodifiableMap(map); + } + + @Override + public boolean isRecursive() { + return true; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java new file mode 100644 index 00000000..c51c7dbf --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/SingletonCommunicationGroup.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication.timeless; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +/** + * A communication group containing only a single node with a single default + * mailbox. + * + * @author Tamas Szabo + * @since 1.6 + */ +public class SingletonCommunicationGroup extends CommunicationGroup { + + private Mailbox mailbox; + + /** + * @since 1.7 + */ + public SingletonCommunicationGroup(final CommunicationTracker tracker, final Node representative, final int identifier) { + super(tracker, representative, identifier); + } + + @Override + public void deliverMessages() { + this.mailbox.deliverAll(PhasedSelector.DEFAULT); + } + + @Override + public boolean isEmpty() { + return this.mailbox == null; + } + + @Override + public void notifyHasMessage(final Mailbox mailbox, final MessageSelector kind) { + if (kind == PhasedSelector.DEFAULT) { + this.mailbox = mailbox; + if (!this.isEnqueued) { + this.tracker.activateUnenqueued(this); + } + } else { + throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind); + } + } + + @Override + public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) { + if (kind == PhasedSelector.DEFAULT) { + this.mailbox = null; + this.tracker.deactivate(this); + } else { + throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind); + } + } + + @Override + public Map> getMailboxes() { + if (mailbox != null) { + return Collections.singletonMap(PhasedSelector.DEFAULT, Collections.singleton(mailbox)); + } else { + return Collections.emptyMap(); + } + } + + @Override + public boolean isRecursive() { + return false; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java new file mode 100644 index 00000000..1c18c1cd --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timeless/TimelessCommunicationTracker.java @@ -0,0 +1,149 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication.timeless; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.rete.index.DualInputNode; +import tools.refinery.viatra.runtime.rete.index.Indexer; +import tools.refinery.viatra.runtime.rete.index.IndexerListener; +import tools.refinery.viatra.runtime.rete.index.IterableIndexer; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.RederivableNode; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; + +/** + * Timeless implementation of the communication tracker. + * + * @author Tamas Szabo + * @since 2.2 + */ +public class TimelessCommunicationTracker extends CommunicationTracker { + + @Override + protected CommunicationGroup createGroup(Node representative, int index) { + final boolean isSingleton = this.sccInformationProvider.sccs.getPartition(representative).size() == 1; + final boolean isReceiver = representative instanceof Receiver; + final boolean isPosetIndifferent = isReceiver + && ((Receiver) representative).getMailbox() instanceof BehaviorChangingMailbox; + final boolean isSingletonInDRedMode = isSingleton && (representative instanceof RederivableNode) + && ((RederivableNode) representative).isInDRedMode(); + + CommunicationGroup group = null; + // we can only use a singleton group iff + // (1) the SCC has one node AND + // (2) either we have a poset-indifferent mailbox OR the node is not even a receiver AND + // (3) the node does not run in DRed mode in a singleton group + if (isSingleton && (isPosetIndifferent || !isReceiver) && !isSingletonInDRedMode) { + group = new SingletonCommunicationGroup(this, representative, index); + } else { + group = new RecursiveCommunicationGroup(this, representative, index); + } + + return group; + } + + @Override + protected void reconstructQueueContents(final Set oldActiveGroups) { + for (final CommunicationGroup oldGroup : oldActiveGroups) { + for (final Entry> entry : oldGroup.getMailboxes().entrySet()) { + for (final Mailbox mailbox : entry.getValue()) { + final CommunicationGroup newGroup = this.groupMap.get(mailbox.getReceiver()); + newGroup.notifyHasMessage(mailbox, entry.getKey()); + } + } + + if (oldGroup instanceof RecursiveCommunicationGroup) { + for (final RederivableNode node : ((RecursiveCommunicationGroup) oldGroup).getRederivables()) { + final CommunicationGroup newGroup = this.groupMap.get(node); + if (!(newGroup instanceof RecursiveCommunicationGroup)) { + throw new IllegalStateException("The new group must also be recursive! " + newGroup); + } + ((RecursiveCommunicationGroup) newGroup).addRederivable(node); + } + } + } + } + + @Override + public Mailbox proxifyMailbox(final Node requester, final Mailbox original) { + return original; + } + + @Override + public IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original) { + return original; + } + + @Override + protected void postProcessNode(final Node node) { + if (node instanceof Receiver) { + final Mailbox mailbox = ((Receiver) node).getMailbox(); + if (mailbox instanceof BehaviorChangingMailbox) { + final CommunicationGroup group = this.groupMap.get(node); + final Set sccNodes = this.sccInformationProvider.sccs.getPartition(node); + // a default mailbox must split its messages iff + // (1) its receiver is in a recursive group and + final boolean c1 = group.isRecursive(); + // (2) its receiver is at the SCC boundary of that group + final boolean c2 = isAtSCCBoundary(node); + // (3) its group consists of more than one node + final boolean c3 = sccNodes.size() > 1; + ((BehaviorChangingMailbox) mailbox).setSplitFlag(c1 && c2 && c3); + } + } + } + + @Override + protected void postProcessGroup(final CommunicationGroup group) { + + } + + /** + * @since 2.0 + */ + private boolean isAtSCCBoundary(final Node node) { + final CommunicationGroup ownGroup = this.groupMap.get(node); + assert ownGroup != null; + for (final Node source : this.dependencyGraph.getSourceNodes(node).distinctValues()) { + final Set sourcesToCheck = new HashSet(); + sourcesToCheck.add(source); + // DualInputNodes must be checked additionally because they do not use a mailbox directly. + // It can happen that their indexers actually belong to other SCCs. + if (source instanceof DualInputNode) { + final DualInputNode dualInput = (DualInputNode) source; + final IterableIndexer primarySlot = dualInput.getPrimarySlot(); + if (primarySlot != null) { + sourcesToCheck.add(primarySlot.getActiveNode()); + } + final Indexer secondarySlot = dualInput.getSecondarySlot(); + if (secondarySlot != null) { + sourcesToCheck.add(secondarySlot.getActiveNode()); + } + } + for (final Node current : sourcesToCheck) { + final CommunicationGroup otherGroup = this.groupMap.get(current); + assert otherGroup != null; + if (!ownGroup.equals(otherGroup)) { + return true; + } + } + } + return false; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java new file mode 100644 index 00000000..8097bd91 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/ResumableNode.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication.timely; + +import tools.refinery.viatra.runtime.rete.network.IGroupable; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * {@link Node}s that implement this interface can resume folding of their states when instructed during timely evaluation. + * + * @since 2.3 + * @author Tamas Szabo + */ +public interface ResumableNode extends Node, IGroupable { + + /** + * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to + * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more + * folding to do towards higher timestamps. + */ + public void resumeAt(final Timestamp timestamp); + + /** + * Returns the smallest timestamp where lazy folding shall be resumed, or null if there is no more folding to do in this + * resumable. + */ + public Timestamp getResumableTimestamp(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java new file mode 100644 index 00000000..0394d92c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationGroup.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication.timely; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; + +import org.apache.log4j.Logger; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; +import tools.refinery.viatra.runtime.rete.util.Options; + +/** + * A timely communication group implementation. {@link TimelyMailbox}es and {@link LazyFoldingNode}s are ordered in the + * increasing order of timestamps. + * + * @author Tamas Szabo + * @since 2.3 + */ +public class TimelyCommunicationGroup extends CommunicationGroup { + + private final boolean isSingleton; + private final TreeMap> mailboxQueue; + // may be null - only used in the scattered case where we need to take care of mailboxes and resumables too + private Comparator nodeComparator; + private boolean currentlyDelivering; + private Timestamp currentlyDeliveredTimestamp; + + public TimelyCommunicationGroup(final TimelyCommunicationTracker tracker, final Node representative, + final int identifier, final boolean isSingleton) { + super(tracker, representative, identifier); + this.isSingleton = isSingleton; + this.mailboxQueue = CollectionsFactory.createTreeMap(); + this.currentlyDelivering = false; + } + + /** + * Sets the {@link Comparator} to be used to order the {@link Mailbox}es at a given {@link Timestamp} in the mailbox + * queue. Additionally, reorders already queued {@link Mailbox}es to reflect the new comparator. The comparator may + * be null, in this case, no set ordering will be enforced among the {@link Mailbox}es. + */ + public void setComparatorAndReorderMailboxes(final Comparator nodeComparator) { + this.nodeComparator = nodeComparator; + if (!this.mailboxQueue.isEmpty()) { + final HashMap> queueCopy = new HashMap>(this.mailboxQueue); + this.mailboxQueue.clear(); + for (final Entry> entry : queueCopy.entrySet()) { + for (final Mailbox mailbox : entry.getValue()) { + this.notifyHasMessage(mailbox, entry.getKey()); + } + } + } + } + + @Override + public void deliverMessages() { + this.currentlyDelivering = true; + while (!this.mailboxQueue.isEmpty()) { + // care must be taken here how we iterate over the mailboxes + // it is not okay to loop over the mailboxes at once because a mailbox may disappear from the collection as + // a result of delivering messages from another mailboxes under the same timestamp + // because of this, it is crucial that we pick the mailboxes one by one + final Entry> entry = this.mailboxQueue.firstEntry(); + final Timestamp timestamp = entry.getKey(); + final Set mailboxes = entry.getValue(); + final Mailbox mailbox = mailboxes.iterator().next(); + mailboxes.remove(mailbox); + if (mailboxes.isEmpty()) { + this.mailboxQueue.pollFirstEntry(); + } + assert mailbox instanceof TimelyMailbox; + /* debug */ this.currentlyDeliveredTimestamp = timestamp; + mailbox.deliverAll(timestamp); + /* debug */ this.currentlyDeliveredTimestamp = null; + } + this.currentlyDelivering = false; + } + + @Override + public boolean isEmpty() { + return this.mailboxQueue.isEmpty(); + } + + @Override + public void notifyHasMessage(final Mailbox mailbox, MessageSelector kind) { + if (kind instanceof Timestamp) { + final Timestamp timestamp = (Timestamp) kind; + if (Options.MONITOR_VIOLATION_OF_DIFFERENTIAL_DATAFLOW_TIMESTAMPS) { + if (timestamp.compareTo(this.currentlyDeliveredTimestamp) < 0) { + final Logger logger = this.representative.getContainer().getNetwork().getEngine().getLogger(); + logger.error( + "[INTERNAL ERROR] Violation of differential dataflow communication schema! The communication component with representative " + + this.representative + " observed decreasing timestamp during message delivery!"); + } + } + final Set mailboxes = this.mailboxQueue.computeIfAbsent(timestamp, k -> { + if (this.nodeComparator == null) { + return CollectionsFactory.createSet(); + } else { + return new TreeSet(new Comparator() { + @Override + public int compare(final Mailbox left, final Mailbox right) { + return nodeComparator.compare(left.getReceiver(), right.getReceiver()); + } + }); + } + }); + mailboxes.add(mailbox); + if (!this.isEnqueued && !this.currentlyDelivering) { + this.tracker.activateUnenqueued(this); + } + } else { + throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind); + } + } + + @Override + public void notifyLostAllMessages(final Mailbox mailbox, final MessageSelector kind) { + if (kind instanceof Timestamp) { + final Timestamp timestamp = (Timestamp) kind; + this.mailboxQueue.compute(timestamp, (k, v) -> { + if (v == null) { + throw new IllegalStateException("No mailboxes registered at timestamp " + timestamp + "!"); + } + if (!v.remove(mailbox)) { + throw new IllegalStateException( + "The mailbox " + mailbox + " was not registered at timestamp " + timestamp + "!"); + } + if (v.isEmpty()) { + return null; + } else { + return v; + } + }); + if (this.mailboxQueue.isEmpty()) { + this.tracker.deactivate(this); + } + } else { + throw new IllegalArgumentException(UNSUPPORTED_MESSAGE_KIND + kind); + } + } + + @Override + public Map> getMailboxes() { + return Collections.unmodifiableMap(this.mailboxQueue); + } + + @Override + public boolean isRecursive() { + return !this.isSingleton; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java new file mode 100644 index 00000000..1ff69882 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java @@ -0,0 +1,216 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication.timely; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.Function; + +import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting; +import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.rete.index.IndexerListener; +import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer; +import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer.ListenerSubscription; +import tools.refinery.viatra.runtime.rete.index.StandardIndexer; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; +import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.ProductionNode; +import tools.refinery.viatra.runtime.rete.network.StandardNode; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.NodeComparator; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.single.DiscriminatorDispatcherNode; + +/** + * Timely (DDF) implementation of the {@link CommunicationTracker}. + * + * @author Tamas Szabo + * @since 2.3 + */ +public class TimelyCommunicationTracker extends CommunicationTracker { + + protected final TimelyConfiguration configuration; + + public TimelyCommunicationTracker(final TimelyConfiguration configuration) { + this.configuration = configuration; + } + + @Override + protected CommunicationGroup createGroup(final Node representative, final int index) { + final boolean isSingleton = this.sccInformationProvider.sccs.getPartition(representative).size() == 1; + return new TimelyCommunicationGroup(this, representative, index, isSingleton); + } + + @Override + protected void reconstructQueueContents(final Set oldActiveGroups) { + for (final CommunicationGroup oldGroup : oldActiveGroups) { + for (final Entry> entry : oldGroup.getMailboxes().entrySet()) { + for (final Mailbox mailbox : entry.getValue()) { + final CommunicationGroup newGroup = this.groupMap.get(mailbox.getReceiver()); + newGroup.notifyHasMessage(mailbox, entry.getKey()); + } + } + } + } + + @Override + public Mailbox proxifyMailbox(final Node requester, final Mailbox original) { + final Mailbox mailboxToProxify = (original instanceof TimelyMailboxProxy) + ? ((TimelyMailboxProxy) original).getWrappedMailbox() + : original; + final TimestampTransformation preprocessor = getPreprocessor(requester, mailboxToProxify.getReceiver()); + if (preprocessor == null) { + return mailboxToProxify; + } else { + return new TimelyMailboxProxy(mailboxToProxify, preprocessor); + } + } + + @Override + public IndexerListener proxifyIndexerListener(final Node requester, final IndexerListener original) { + final IndexerListener listenerToProxify = (original instanceof TimelyIndexerListenerProxy) + ? ((TimelyIndexerListenerProxy) original).getWrappedIndexerListener() + : original; + final TimestampTransformation preprocessor = getPreprocessor(requester, listenerToProxify.getOwner()); + if (preprocessor == null) { + return listenerToProxify; + } else { + return new TimelyIndexerListenerProxy(listenerToProxify, preprocessor); + } + } + + protected TimestampTransformation getPreprocessor(final Node source, final Node target) { + final Node effectiveSource = source instanceof SpecializedProjectionIndexer + ? ((SpecializedProjectionIndexer) source).getActiveNode() + : source; + final CommunicationGroup sourceGroup = this.getGroup(effectiveSource); + final CommunicationGroup targetGroup = this.getGroup(target); + + if (sourceGroup != null && targetGroup != null) { + // during RETE construction, the groups may be still null + if (sourceGroup != targetGroup && sourceGroup.isRecursive()) { + // targetGroup is a successor SCC of sourceGroup + // and sourceGroup is a recursive SCC + // then we need to zero out the timestamps + return TimestampTransformation.RESET; + } + if (sourceGroup == targetGroup && target instanceof ProductionNode) { + // if requester and receiver are in the same SCC + // and receiver is a production node + // then we need to increment the timestamps + return TimestampTransformation.INCREMENT; + } + } + + return null; + } + + @Override + protected void postProcessNode(final Node node) { + if (node instanceof NetworkStructureChangeSensitiveNode) { + ((NetworkStructureChangeSensitiveNode) node).networkStructureChanged(); + } + } + + @Override + protected void postProcessGroup(final CommunicationGroup group) { + if (this.configuration.getTimelineRepresentation() == TimelineRepresentation.FAITHFUL) { + final Node representative = group.getRepresentative(); + final Set groupMembers = this.sccInformationProvider.sccs.getPartition(representative); + if (groupMembers.size() > 1) { + final Graph graph = new Graph(); + + for (final Node node : groupMembers) { + graph.insertNode(node); + } + + for (final Node source : groupMembers) { + for (final Node target : this.dependencyGraph.getTargetNodes(source)) { + // (1) the edge is not a recursion cut point + // (2) the edge is within this group + if (!this.isRecursionCutPoint(source, target) && groupMembers.contains(target)) { + graph.insertEdge(source, target); + } + } + } + + final List orderedNodes = TopologicalSorting.compute(graph); + final Map nodeMap = CollectionsFactory.createMap(); + int identifier = 0; + for (final Node orderedNode : orderedNodes) { + nodeMap.put(orderedNode, identifier++); + } + + ((TimelyCommunicationGroup) group).setComparatorAndReorderMailboxes(new NodeComparator(nodeMap)); + } + } + } + + /** + * This static field is used for debug purposes in the DotGenerator. + */ + public static final Function> EDGE_LABEL_FUNCTION = new Function>() { + + @Override + public Function apply(final Node source) { + return new Function() { + @Override + public String apply(final Node target) { + if (source instanceof SpecializedProjectionIndexer) { + final Collection subscriptions = ((SpecializedProjectionIndexer) source) + .getSubscriptions(); + for (final ListenerSubscription subscription : subscriptions) { + if (subscription.getListener().getOwner() == target + && subscription.getListener() instanceof TimelyIndexerListenerProxy) { + return ((TimelyIndexerListenerProxy) subscription.getListener()).preprocessor + .toString(); + } + } + } + if (source instanceof StandardIndexer) { + final Collection listeners = ((StandardIndexer) source).getListeners(); + for (final IndexerListener listener : listeners) { + if (listener.getOwner() == target && listener instanceof TimelyIndexerListenerProxy) { + return ((TimelyIndexerListenerProxy) listener).preprocessor.toString(); + } + } + } + if (source instanceof StandardNode) { + final Collection mailboxes = ((StandardNode) source).getChildMailboxes(); + for (final Mailbox mailbox : mailboxes) { + if (mailbox.getReceiver() == target && mailbox instanceof TimelyMailboxProxy) { + return ((TimelyMailboxProxy) mailbox).preprocessor.toString(); + } + } + } + if (source instanceof DiscriminatorDispatcherNode) { + final Collection mailboxes = ((DiscriminatorDispatcherNode) source) + .getBucketMailboxes().values(); + for (final Mailbox mailbox : mailboxes) { + if (mailbox.getReceiver() == target && mailbox instanceof TimelyMailboxProxy) { + return ((TimelyMailboxProxy) mailbox).preprocessor.toString(); + } + } + } + return null; + } + }; + } + + }; + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java new file mode 100644 index 00000000..e8fbf84e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyIndexerListenerProxy.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication.timely; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; +import tools.refinery.viatra.runtime.rete.index.IndexerListener; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.ProductionNode; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * A timely proxy for another {@link IndexerListener}, which performs some preprocessing + * on the differential timestamps before passing it on to the real recipient. + *

+ * These proxies are used on edges leading into {@link ProductionNode}s. Because {@link ProductionNode}s + * never ask back the indexer for its contents, there is no need to also apply the proxy on that direction. + * + * @author Tamas Szabo + * @since 2.3 + */ +public class TimelyIndexerListenerProxy implements IndexerListener { + + protected final TimestampTransformation preprocessor; + protected final IndexerListener wrapped; + + public TimelyIndexerListenerProxy(final IndexerListener wrapped, + final TimestampTransformation preprocessor) { + Preconditions.checkArgument(!(wrapped instanceof TimelyIndexerListenerProxy), "Proxy in a proxy is not allowed!"); + this.wrapped = wrapped; + this.preprocessor = preprocessor; + } + + public IndexerListener getWrappedIndexerListener() { + return wrapped; + } + + @Override + public Node getOwner() { + return this.wrapped.getOwner(); + } + + @Override + public void notifyIndexerUpdate(final Direction direction, final Tuple updateElement, final Tuple signature, + final boolean change, final Timestamp timestamp) { + this.wrapped.notifyIndexerUpdate(direction, updateElement, signature, change, preprocessor.process(timestamp)); + } + + @Override + public String toString() { + return this.preprocessor.toString() + "_PROXY -> " + this.wrapped.toString(); + } + + @Override + public boolean equals(final Object obj) { + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } else if (obj == this) { + return true; + } else { + final TimelyIndexerListenerProxy that = (TimelyIndexerListenerProxy) obj; + return this.wrapped.equals(that.wrapped) && this.preprocessor == that.preprocessor; + } + } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 17 + this.wrapped.hashCode(); + hash = hash * 31 + this.preprocessor.hashCode(); + return hash; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java new file mode 100644 index 00000000..550bfbeb --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyMailboxProxy.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication.timely; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +/** + * A timely proxy for another {@link Mailbox}, which performs some preprocessing + * on the differential timestamps before passing it on to the real recipient. + * + * @author Tamas Szabo + * @since 2.3 + */ +public class TimelyMailboxProxy implements Mailbox { + + protected final TimestampTransformation preprocessor; + protected final Mailbox wrapped; + + public TimelyMailboxProxy(final Mailbox wrapped, final TimestampTransformation preprocessor) { + Preconditions.checkArgument(!(wrapped instanceof TimelyMailboxProxy), "Proxy in a proxy is not allowed!"); + this.wrapped = wrapped; + this.preprocessor = preprocessor; + } + + public Mailbox getWrappedMailbox() { + return wrapped; + } + + @Override + public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { + this.wrapped.postMessage(direction, update, preprocessor.process(timestamp)); + } + + @Override + public String toString() { + return this.preprocessor.toString() + "_PROXY -> " + this.wrapped.toString(); + } + + @Override + public void clear() { + this.wrapped.clear(); + } + + @Override + public void deliverAll(final MessageSelector selector) { + this.wrapped.deliverAll(selector); + } + + @Override + public CommunicationGroup getCurrentGroup() { + return this.wrapped.getCurrentGroup(); + } + + @Override + public void setCurrentGroup(final CommunicationGroup group) { + this.wrapped.setCurrentGroup(group); + } + + @Override + public Receiver getReceiver() { + return this.wrapped.getReceiver(); + } + + @Override + public boolean isEmpty() { + return this.wrapped.isEmpty(); + } + + @Override + public boolean equals(final Object obj) { + if (obj == null || obj.getClass() != this.getClass()) { + return false; + } else if (obj == this) { + return true; + } else { + final TimelyMailboxProxy that = (TimelyMailboxProxy) obj; + return this.wrapped.equals(that.wrapped) && this.preprocessor == that.preprocessor; + } + } + + @Override + public int hashCode() { + int hash = 1; + hash = hash * 17 + this.wrapped.hashCode(); + hash = hash * 31 + this.preprocessor.hashCode(); + return hash; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java new file mode 100644 index 00000000..8929eb5c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimestampTransformation.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.communication.timely; + +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * Values of this enum perform different kind of preprocessing on {@link Timestamp}s. + * This is used on edges leading in and out from {@link Node}s in recursive {@link TimelyCommunicationGroup}s. + * + * @author Tamas Szabo + * @since 2.3 + */ +public enum TimestampTransformation { + + INCREMENT { + @Override + public Timestamp process(final Timestamp timestamp) { + return new Timestamp(timestamp.getValue() + 1); + } + + @Override + public String toString() { + return "INCREMENT"; + } + }, + RESET { + @Override + public Timestamp process(final Timestamp timestamp) { + return Timestamp.ZERO; + } + + @Override + public String toString() { + return "RESET"; + } + }; + + public abstract Timestamp process(final Timestamp timestamp); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java new file mode 100644 index 00000000..d6312671 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedCommand.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.delayed; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.Network; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +/** + * Instances of this class are responsible for initializing a {@link Receiver} with the contents of a {@link Supplier}. + * However, due to the dynamic nature of the Rete {@link Network} and to the fact that certain {@link Node}s in the + * {@link Network} are sensitive to the shape of the {@link Network}, the commands must be delayed until the + * construction of the {@link Network} has stabilized. + * + * @author Tamas Szabo + * @since 2.3 + */ +public abstract class DelayedCommand implements Runnable { + + protected final Supplier supplier; + protected final Receiver receiver; + protected final Direction direction; + protected final ReteContainer container; + + public DelayedCommand(final Supplier supplier, final Receiver receiver, final Direction direction, + final ReteContainer container) { + this.supplier = supplier; + this.receiver = receiver; + this.direction = direction; + this.container = container; + } + + @Override + public void run() { + final CommunicationTracker tracker = this.container.getCommunicationTracker(); + final Mailbox mailbox = tracker.proxifyMailbox(this.supplier, this.receiver.getMailbox()); + + if (this.isTimestampAware()) { + final Map> contents = this.container.pullContentsWithTimeline(this.supplier, + false); + for (final Entry> entry : contents.entrySet()) { + for (final Signed change : entry.getValue().asChangeSequence()) { + mailbox.postMessage(change.getDirection().multiply(this.direction), entry.getKey(), + change.getPayload()); + } + } + } else { + final Collection contents = this.container.pullContents(this.supplier, false); + for (final Tuple tuple : contents) { + mailbox.postMessage(this.direction, tuple, Timestamp.ZERO); + } + } + } + + @Override + public String toString() { + return this.supplier + " -> " + this.receiver.toString(); + } + + protected abstract boolean isTimestampAware(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java new file mode 100644 index 00000000..1bfdbec6 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedConnectCommand.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.delayed; + +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; + +public class DelayedConnectCommand extends DelayedCommand { + + public DelayedConnectCommand(final Supplier supplier, final Receiver receiver, final ReteContainer container) { + super(supplier, receiver, Direction.INSERT, container); + } + + @Override + protected boolean isTimestampAware() { + return this.container.isTimelyEvaluation() && this.container.getCommunicationTracker().areInSameGroup(this.supplier, this.receiver); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java new file mode 100644 index 00000000..5825a971 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/delayed/DelayedDisconnectCommand.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.delayed; + +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; + +public class DelayedDisconnectCommand extends DelayedCommand { + + protected final boolean wasInSameSCC; + + public DelayedDisconnectCommand(final Supplier supplier, final Receiver receiver, final ReteContainer container, final boolean wasInSameSCC) { + super(supplier, receiver, Direction.DELETE, container); + this.wasInSameSCC = wasInSameSCC; + } + + @Override + protected boolean isTimestampAware() { + return this.wasInSameSCC; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java new file mode 100644 index 00000000..da9bc47e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/DefaultMessageIndexer.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.indexer; + +import java.util.Collections; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; + +/** + * @author Tamas Szabo + * @since 2.0 + */ +public class DefaultMessageIndexer implements MessageIndexer { + + protected final Map indexer; + + public DefaultMessageIndexer() { + this.indexer = CollectionsFactory.createMap(); + } + + public Map getTuples() { + return Collections.unmodifiableMap(this.indexer); + } + + @Override + public int getCount(final Tuple update) { + final Integer count = getTuples().get(update); + if (count == null) { + return 0; + } else { + return count; + } + } + + @Override + public void insert(final Tuple update) { + update(update, 1); + } + + @Override + public void delete(final Tuple update) { + update(update, -1); + } + + @Override + public void update(final Tuple update, final int delta) { + final Integer oldCount = this.indexer.get(update); + final int newCount = (oldCount == null ? 0 : oldCount) + delta; + if (newCount == 0) { + this.indexer.remove(update); + } else { + this.indexer.put(update, newCount); + } + } + + @Override + public boolean isEmpty() { + return this.indexer.isEmpty(); + } + + @Override + public void clear() { + this.indexer.clear(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java new file mode 100644 index 00000000..80271252 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/GroupBasedMessageIndexer.java @@ -0,0 +1,95 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.indexer; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; + +/** + * @author Tamas Szabo + * @since 2.0 + */ +public class GroupBasedMessageIndexer implements MessageIndexer { + + protected final Map indexer; + protected final TupleMask groupMask; + + public GroupBasedMessageIndexer(final TupleMask groupMask) { + this.indexer = CollectionsFactory.createMap(); + this.groupMask = groupMask; + } + + public Map getTuplesByGroup(final Tuple group) { + final DefaultMessageIndexer values = this.indexer.get(group); + if (values == null) { + return Collections.emptyMap(); + } else { + return Collections.unmodifiableMap(values.getTuples()); + } + } + + @Override + public int getCount(final Tuple update) { + final Tuple group = this.groupMask.transform(update); + final Integer count = getTuplesByGroup(group).get(update); + if (count == null) { + return 0; + } else { + return count; + } + } + + public Set getGroups() { + return Collections.unmodifiableSet(this.indexer.keySet()); + } + + @Override + public void insert(final Tuple update) { + update(update, 1); + } + + @Override + public void delete(final Tuple update) { + update(update, -1); + } + + @Override + public void update(final Tuple update, final int delta) { + final Tuple group = this.groupMask.transform(update); + DefaultMessageIndexer valueIndexer = this.indexer.get(group); + + if (valueIndexer == null) { + valueIndexer = new DefaultMessageIndexer(); + this.indexer.put(group, valueIndexer); + } + + valueIndexer.update(update, delta); + + // it may happen that the indexer becomes empty as a result of the update + if (valueIndexer.isEmpty()) { + this.indexer.remove(group); + } + } + + @Override + public boolean isEmpty() { + return this.indexer.isEmpty(); + } + + @Override + public void clear() { + this.indexer.clear(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java new file mode 100644 index 00000000..271aaa44 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/indexer/MessageIndexer.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.indexer; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Clearable; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +/** + * A message indexer is used by {@link Mailbox}es to index their contents. + * + * @author Tamas Szabo + * @since 2.0 + */ +public interface MessageIndexer extends Clearable { + + public void insert(final Tuple update); + + public void delete(final Tuple update); + + public void update(final Tuple update, final int delta); + + public boolean isEmpty(); + + public int getCount(final Tuple update); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java new file mode 100644 index 00000000..99097f56 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/AdaptableMailbox.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.mailbox; + +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyMailboxProxy; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; + +/** + * An adaptable mailbox can be wrapped by another mailbox to act in behalf of that. The significance of the adaptation + * is that the adaptee will notify the {@link CommunicationTracker} about updates by promoting the adapter itself. + * Adaptable mailboxes are used by the {@link BehaviorChangingMailbox}. + * + * Compare this with {@link TimelyMailboxProxy}. That one also wraps another mailbox in order to + * perform preprocessing on the messages sent to the original recipient. + * + * @author Tamas Szabo + * @since 2.0 + */ +public interface AdaptableMailbox extends Mailbox { + + public Mailbox getAdapter(); + + public void setAdapter(final Mailbox adapter); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java new file mode 100644 index 00000000..8797e254 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/FallThroughCapableMailbox.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.mailbox; + +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; + +/** + * A fall through capable mailbox can directly call the update method of its {@link Receiver} instead of using the + * standard post-deliver mailbox semantics. If the fall through flag is set to true, the mailbox uses direct delivery, + * otherwise it operates in the original behavior. The fall through operation is preferable whenever applicable because + * it improves performance. The fall through flag is controlled by the {@link CommunicationTracker} based on the + * receiver node type and network topology. + * + * @author Tamas Szabo + * @since 2.2 + */ +public interface FallThroughCapableMailbox extends Mailbox { + + public boolean isFallThrough(); + + public void setFallThrough(final boolean fallThrough); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java new file mode 100644 index 00000000..05005974 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/Mailbox.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.mailbox; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Clearable; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.IGroupable; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * A mailbox is associated with every {@link Receiver}. Messages can be sent to a {@link Receiver} by posting them into + * the mailbox. Different mailbox implementations may differ in the way how they deliver the posted messages. + * + * @author Tamas Szabo + * @since 2.0 + * + */ +public interface Mailbox extends Clearable, IGroupable { + + /** + * Posts a new message to this mailbox. + * + * @param direction + * the direction of the update + * @param update + * the update element + * @since 2.4 + */ + public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp); + + /** + * Delivers all messages according to the given selector from this mailbox. The selector can also be null. In this case, no + * special separation is expected between the messages. + * + * @param selector the message selector + */ + public void deliverAll(final MessageSelector selector); + + /** + * Returns the {@link Receiver} of this mailbox. + * + * @return the receiver + */ + public Receiver getReceiver(); + + /** + * Returns the {@link CommunicationGroup} of the receiver of this mailbox. + * + * @return the communication group + */ + public CommunicationGroup getCurrentGroup(); + + /** + * Sets the {@link CommunicationGroup} that the receiver of this mailbox is associated with. + * + * @param group + * the communication group + */ + public void setCurrentGroup(final CommunicationGroup group); + + /** + * Returns true if this mailbox is empty. + * + * @return + */ + public boolean isEmpty(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java new file mode 100644 index 00000000..2c5255fb --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/MessageIndexerFactory.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.mailbox; + +import tools.refinery.viatra.runtime.rete.network.indexer.MessageIndexer; + +/** + * A factory used to create message indexers for {@link Mailbox}es. + * + * @author Tamas Szabo + * @since 2.0 + */ +public interface MessageIndexerFactory { + + public I create(); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java new file mode 100644 index 00000000..1e1ada71 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/AbstractUpdateSplittingMailbox.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.mailbox.timeless; + +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.indexer.MessageIndexer; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.MessageIndexerFactory; + +/** + * An abstract mailbox implementation that is capable of splitting update messages based on some form of monotonicity + * (anti-monotone and monotone). The monotonicity is either defined by the less or equal operator of a poset or, it can + * be the standard subset ordering among sets of tuples. + * + * @author Tamas Szabo + * @since 2.0 + * + */ +public abstract class AbstractUpdateSplittingMailbox implements Mailbox { + + protected IndexerType monotoneQueue; + protected IndexerType antiMonotoneQueue; + protected IndexerType monotoneBuffer; + protected IndexerType antiMonotoneBuffer; + protected boolean deliveringMonotone; + protected boolean deliveringAntiMonotone; + protected final ReceiverType receiver; + protected final ReteContainer container; + protected CommunicationGroup group; + + public AbstractUpdateSplittingMailbox(final ReceiverType receiver, final ReteContainer container, + final MessageIndexerFactory factory) { + this.receiver = receiver; + this.container = container; + this.monotoneQueue = factory.create(); + this.antiMonotoneQueue = factory.create(); + this.monotoneBuffer = factory.create(); + this.antiMonotoneBuffer = factory.create(); + this.deliveringMonotone = false; + this.deliveringAntiMonotone = false; + } + + protected void swapAndClearMonotone() { + final IndexerType tmp = this.monotoneQueue; + this.monotoneQueue = this.monotoneBuffer; + this.monotoneBuffer = tmp; + this.monotoneBuffer.clear(); + } + + protected void swapAndClearAntiMonotone() { + final IndexerType tmp = this.antiMonotoneQueue; + this.antiMonotoneQueue = this.antiMonotoneBuffer; + this.antiMonotoneBuffer = tmp; + this.antiMonotoneBuffer.clear(); + } + + protected IndexerType getActiveMonotoneQueue() { + if (this.deliveringMonotone) { + return this.monotoneBuffer; + } else { + return this.monotoneQueue; + } + } + + protected IndexerType getActiveAntiMonotoneQueue() { + if (this.deliveringAntiMonotone) { + return this.antiMonotoneBuffer; + } else { + return this.antiMonotoneQueue; + } + } + + @Override + public ReceiverType getReceiver() { + return this.receiver; + } + + @Override + public void clear() { + this.monotoneQueue.clear(); + this.antiMonotoneQueue.clear(); + this.monotoneBuffer.clear(); + this.antiMonotoneBuffer.clear(); + } + + @Override + public boolean isEmpty() { + return this.getActiveMonotoneQueue().isEmpty() && this.getActiveAntiMonotoneQueue().isEmpty(); + } + + @Override + public CommunicationGroup getCurrentGroup() { + return this.group; + } + + @Override + public void setCurrentGroup(final CommunicationGroup group) { + this.group = group; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java new file mode 100644 index 00000000..fe822d7c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/BehaviorChangingMailbox.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.mailbox.timeless; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timeless.TimelessCommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.FallThroughCapableMailbox; + +/** + * This mailbox changes its behavior based on the position of its {@link Receiver} in the network topology. + * It either behaves as a {@link DefaultMailbox} or as an {@link UpdateSplittingMailbox}. The decision is made by the + * {@link CommunicationTracker}, see {@link TimelessCommunicationTracker#postProcessNode(Node)} for more details. + * + * @author Tamas Szabo + */ +public class BehaviorChangingMailbox implements FallThroughCapableMailbox { + + protected boolean fallThrough; + protected boolean split; + protected AdaptableMailbox wrapped; + protected final Receiver receiver; + protected final ReteContainer container; + protected CommunicationGroup group; + + public BehaviorChangingMailbox(final Receiver receiver, final ReteContainer container) { + this.fallThrough = false; + this.split = false; + this.receiver = receiver; + this.container = container; + this.wrapped = new DefaultMailbox(receiver, container); + this.wrapped.setAdapter(this); + } + + @Override + public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { + if (this.fallThrough && !this.container.isExecutingDelayedCommands()) { + // disable fall through while we are in the middle of executing delayed construction commands + this.receiver.update(direction, update, timestamp); + } else { + this.wrapped.postMessage(direction, update, timestamp); + } + } + + @Override + public void deliverAll(final MessageSelector kind) { + this.wrapped.deliverAll(kind); + } + + @Override + public String toString() { + return "A_MBOX -> " + this.wrapped; + } + + public void setSplitFlag(final boolean splitValue) { + if (this.split != splitValue) { + assert isEmpty(); + if (splitValue) { + this.wrapped = new UpdateSplittingMailbox(this.receiver, this.container); + } else { + this.wrapped = new DefaultMailbox(this.receiver, this.container); + } + this.wrapped.setAdapter(this); + this.split = splitValue; + } + } + + @Override + public boolean isEmpty() { + return this.wrapped.isEmpty(); + } + + @Override + public void clear() { + this.wrapped.clear(); + } + + @Override + public Receiver getReceiver() { + return this.receiver; + } + + @Override + public CommunicationGroup getCurrentGroup() { + return this.group; + } + + @Override + public void setCurrentGroup(final CommunicationGroup group) { + this.group = group; + } + + @Override + public boolean isFallThrough() { + return this.fallThrough; + } + + @Override + public void setFallThrough(final boolean fallThrough) { + this.fallThrough = fallThrough; + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java new file mode 100644 index 00000000..5c72ba39 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/DefaultMailbox.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.mailbox.timeless; + +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +/** + * Default mailbox implementation. + *

+ * Usually, the mailbox performs counting of messages so that they can cancel each other out. However, if marked as a + * fall-through mailbox, than update messages are delivered directly to the receiver node to reduce overhead. + * + * @author Tamas Szabo + * @since 2.0 + */ +public class DefaultMailbox implements AdaptableMailbox { + + private static int SIZE_TRESHOLD = 127; + + protected Map queue; + protected Map buffer; + protected final Receiver receiver; + protected final ReteContainer container; + protected boolean delivering; + protected Mailbox adapter; + protected CommunicationGroup group; + + public DefaultMailbox(final Receiver receiver, final ReteContainer container) { + this.receiver = receiver; + this.container = container; + this.queue = CollectionsFactory.createMap(); + this.buffer = CollectionsFactory.createMap(); + this.adapter = this; + } + + protected Map getActiveQueue() { + if (this.delivering) { + return this.buffer; + } else { + return this.queue; + } + } + + @Override + public Mailbox getAdapter() { + return this.adapter; + } + + @Override + public void setAdapter(final Mailbox adapter) { + this.adapter = adapter; + } + + @Override + public boolean isEmpty() { + return getActiveQueue().isEmpty(); + } + + @Override + public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { + final Map activeQueue = getActiveQueue(); + final boolean wasEmpty = activeQueue.isEmpty(); + + boolean significantChange = false; + Integer count = activeQueue.get(update); + if (count == null) { + count = 0; + significantChange = true; + } + + if (direction == Direction.DELETE) { + count--; + } else { + count++; + } + + if (count == 0) { + activeQueue.remove(update); + significantChange = true; + } else { + activeQueue.put(update, count); + } + + if (significantChange) { + final Mailbox targetMailbox = this.adapter; + final CommunicationGroup targetGroup = this.adapter.getCurrentGroup(); + + if (wasEmpty) { + targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.DEFAULT); + } else if (activeQueue.isEmpty()) { + targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.DEFAULT); + } + } + } + + @Override + public void deliverAll(final MessageSelector kind) { + if (kind == PhasedSelector.DEFAULT) { + // use the buffer during delivering so that there is a clear + // separation between the stages + this.delivering = true; + this.receiver.batchUpdate(this.queue.entrySet(), Timestamp.ZERO); + this.delivering = false; + + if (queue.size() > SIZE_TRESHOLD) { + this.queue = this.buffer; + this.buffer = CollectionsFactory.createMap(); + } else { + this.queue.clear(); + final Map tmpQueue = this.queue; + this.queue = this.buffer; + this.buffer = tmpQueue; + } + } else { + throw new IllegalArgumentException("Unsupported message kind " + kind); + } + } + + @Override + public String toString() { + return "D_MBOX (" + this.receiver + ") " + this.getActiveQueue(); + } + + @Override + public Receiver getReceiver() { + return this.receiver; + } + + @Override + public void clear() { + this.queue.clear(); + this.buffer.clear(); + } + + @Override + public CommunicationGroup getCurrentGroup() { + return this.group; + } + + @Override + public void setCurrentGroup(final CommunicationGroup group) { + this.group = group; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java new file mode 100644 index 00000000..50d19882 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/PosetAwareMailbox.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.mailbox.timeless; + +import java.util.HashSet; +import java.util.Map.Entry; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.indexer.GroupBasedMessageIndexer; + +/** + * A monotonicity aware mailbox implementation. The mailbox uses an {@link IPosetComparator} to identify if a pair of + * REVOKE - INSERT updates represent a monotone change pair. The mailbox is used by {@link PosetAwareReceiver}s. + * + * @author Tamas Szabo + * @since 2.0 + */ +public class PosetAwareMailbox extends AbstractUpdateSplittingMailbox { + + protected final TupleMask groupMask; + + public PosetAwareMailbox(final PosetAwareReceiver receiver, final ReteContainer container) { + super(receiver, container, () -> new GroupBasedMessageIndexer(receiver.getCoreMask())); + this.groupMask = receiver.getCoreMask(); + } + + @Override + public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { + final GroupBasedMessageIndexer monotoneQueue = getActiveMonotoneQueue(); + final GroupBasedMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue(); + final boolean wasPresentAsMonotone = monotoneQueue.getCount(update) != 0; + final boolean wasPresentAsAntiMonotone = antiMonotoneQueue.getCount(update) != 0; + final TupleMask coreMask = this.receiver.getCoreMask(); + + // it cannot happen that it was present in both + assert !(wasPresentAsMonotone && wasPresentAsAntiMonotone); + + if (direction == Direction.INSERT) { + if (wasPresentAsAntiMonotone) { + // it was an anti-monotone one before + antiMonotoneQueue.insert(update); + } else { + // it was a monotone one before or did not exist at all + monotoneQueue.insert(update); + + // if it was not present in the monotone queue before, then + // we need to check whether it makes REVOKE updates monotone + if (!wasPresentAsMonotone) { + final Set counterParts = tryFindCounterPart(update, false, true); + for (final Tuple counterPart : counterParts) { + final int count = antiMonotoneQueue.getCount(counterPart); + assert count < 0; + antiMonotoneQueue.update(counterPart, -count); + monotoneQueue.update(counterPart, count); + } + } + } + } else { + if (wasPresentAsAntiMonotone) { + // it was an anti-monotone one before + antiMonotoneQueue.delete(update); + } else if (wasPresentAsMonotone) { + // it was a monotone one before + monotoneQueue.delete(update); + + // and we need to check whether the monotone REVOKE updates + // still have a reinforcing counterpart + final Set candidates = new HashSet(); + final Tuple key = coreMask.transform(update); + for (final Entry entry : monotoneQueue.getTuplesByGroup(key).entrySet()) { + if (entry.getValue() < 0) { + final Tuple candidate = entry.getKey(); + final Set counterParts = tryFindCounterPart(candidate, true, false); + if (counterParts.isEmpty()) { + // all of them are gone + candidates.add(candidate); + } + } + } + + // move the candidates from the monotone queue to the + // anti-monotone queue because they do not have a + // counterpart anymore + for (final Tuple candidate : candidates) { + final int count = monotoneQueue.getCount(candidate); + assert count < 0; + monotoneQueue.update(candidate, -count); + antiMonotoneQueue.update(candidate, count); + } + } else { + // it did not exist before + final Set counterParts = tryFindCounterPart(update, true, false); + if (counterParts.isEmpty()) { + // there is no tuple that would make this update monotone + antiMonotoneQueue.delete(update); + } else { + // there is a reinforcing counterpart + monotoneQueue.delete(update); + } + } + } + + if (antiMonotoneQueue.isEmpty()) { + this.group.notifyLostAllMessages(this, PhasedSelector.ANTI_MONOTONE); + } else { + this.group.notifyHasMessage(this, PhasedSelector.ANTI_MONOTONE); + } + + if (monotoneQueue.isEmpty()) { + this.group.notifyLostAllMessages(this, PhasedSelector.MONOTONE); + } else { + this.group.notifyHasMessage(this, PhasedSelector.MONOTONE); + } + } + + protected Set tryFindCounterPart(final Tuple first, final boolean findPositiveCounterPart, + final boolean findAllCounterParts) { + final GroupBasedMessageIndexer monotoneQueue = getActiveMonotoneQueue(); + final GroupBasedMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue(); + final TupleMask coreMask = this.receiver.getCoreMask(); + final TupleMask posetMask = this.receiver.getPosetMask(); + final IPosetComparator posetComparator = this.receiver.getPosetComparator(); + final Set result = CollectionsFactory.createSet(); + final Tuple firstKey = coreMask.transform(first); + final Tuple firstValue = posetMask.transform(first); + + if (findPositiveCounterPart) { + for (final Entry entry : monotoneQueue.getTuplesByGroup(firstKey).entrySet()) { + final Tuple secondValue = posetMask.transform(entry.getKey()); + if (entry.getValue() > 0 && posetComparator.isLessOrEqual(firstValue, secondValue)) { + result.add(entry.getKey()); + if (!findAllCounterParts) { + return result; + } + } + } + } else { + for (final Entry entry : antiMonotoneQueue.getTuplesByGroup(firstKey).entrySet()) { + final Tuple secondValue = posetMask.transform(entry.getKey()); + if (posetComparator.isLessOrEqual(secondValue, firstValue)) { + result.add(entry.getKey()); + if (!findAllCounterParts) { + return result; + } + } + } + } + + return result; + } + + @Override + public void deliverAll(final MessageSelector kind) { + if (kind == PhasedSelector.ANTI_MONOTONE) { + // use the buffer during delivering so that there is a clear + // separation between the stages + this.deliveringAntiMonotone = true; + + for (final Tuple group : this.antiMonotoneQueue.getGroups()) { + for (final Entry entry : this.antiMonotoneQueue.getTuplesByGroup(group).entrySet()) { + final Tuple update = entry.getKey(); + final int count = entry.getValue(); + assert count < 0; + for (int i = 0; i < Math.abs(count); i++) { + this.receiver.updateWithPosetInfo(Direction.DELETE, update, false); + } + } + } + + this.deliveringAntiMonotone = false; + swapAndClearAntiMonotone(); + } else if (kind == PhasedSelector.MONOTONE) { + // use the buffer during delivering so that there is a clear + // separation between the stages + this.deliveringMonotone = true; + + for (final Tuple group : this.monotoneQueue.getGroups()) { + for (final Entry entry : this.monotoneQueue.getTuplesByGroup(group).entrySet()) { + final Tuple update = entry.getKey(); + final int count = entry.getValue(); + assert count != 0; + final Direction direction = count < 0 ? Direction.DELETE : Direction.INSERT; + for (int i = 0; i < Math.abs(count); i++) { + this.receiver.updateWithPosetInfo(direction, update, true); + } + } + } + + this.deliveringMonotone = false; + swapAndClearMonotone(); + } else { + throw new IllegalArgumentException("Unsupported message kind " + kind); + } + } + + @Override + public String toString() { + return "PA_MBOX (" + this.receiver + ") " + this.getActiveMonotoneQueue() + " " + + this.getActiveAntiMonotoneQueue(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java new file mode 100644 index 00000000..afa155b2 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timeless/UpdateSplittingMailbox.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.mailbox.timeless; + +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.PhasedSelector; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.indexer.DefaultMessageIndexer; +import tools.refinery.viatra.runtime.rete.network.mailbox.AdaptableMailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +/** + * A mailbox implementation that splits updates messages according to the standard subset ordering into anti-monotonic + * (deletions) and monotonic (insertions) updates. + * + * @author Tamas Szabo + * @since 2.0 + */ +public class UpdateSplittingMailbox extends AbstractUpdateSplittingMailbox + implements AdaptableMailbox { + + protected Mailbox adapter; + + public UpdateSplittingMailbox(final Receiver receiver, final ReteContainer container) { + super(receiver, container, DefaultMessageIndexer::new); + this.adapter = this; + } + + @Override + public Mailbox getAdapter() { + return this.adapter; + } + + @Override + public void setAdapter(final Mailbox adapter) { + this.adapter = adapter; + } + + @Override + public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { + final DefaultMessageIndexer monotoneQueue = getActiveMonotoneQueue(); + final DefaultMessageIndexer antiMonotoneQueue = getActiveAntiMonotoneQueue(); + final boolean wasPresentAsMonotone = monotoneQueue.getCount(update) != 0; + final boolean wasPresentAsAntiMonotone = antiMonotoneQueue.getCount(update) != 0; + + // it cannot happen that it was present in both + assert !(wasPresentAsMonotone && wasPresentAsAntiMonotone); + + if (direction == Direction.INSERT) { + if (wasPresentAsAntiMonotone) { + // it was an anti-monotone one before + antiMonotoneQueue.insert(update); + } else { + // it was a monotone one before or did not exist at all + monotoneQueue.insert(update); + } + } else { + if (wasPresentAsMonotone) { + // it was a monotone one before + monotoneQueue.delete(update); + } else { + // it was an anti-monotone one before or did not exist at all + antiMonotoneQueue.delete(update); + } + } + + final Mailbox targetMailbox = this.adapter; + final CommunicationGroup targetGroup = this.adapter.getCurrentGroup(); + + if (antiMonotoneQueue.isEmpty()) { + targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.ANTI_MONOTONE); + } else { + targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.ANTI_MONOTONE); + } + + if (monotoneQueue.isEmpty()) { + targetGroup.notifyLostAllMessages(targetMailbox, PhasedSelector.MONOTONE); + } else { + targetGroup.notifyHasMessage(targetMailbox, PhasedSelector.MONOTONE); + } + } + + @Override + public void deliverAll(final MessageSelector kind) { + if (kind == PhasedSelector.ANTI_MONOTONE) { + // deliver anti-monotone + this.deliveringAntiMonotone = true; + for (final Entry entry : this.antiMonotoneQueue.getTuples().entrySet()) { + final Tuple update = entry.getKey(); + final int count = entry.getValue(); + assert count < 0; + for (int i = 0; i < Math.abs(count); i++) { + this.receiver.update(Direction.DELETE, update, Timestamp.ZERO); + } + } + this.deliveringAntiMonotone = false; + swapAndClearAntiMonotone(); + } else if (kind == PhasedSelector.MONOTONE) { + // deliver monotone + this.deliveringMonotone = true; + for (final Entry entry : this.monotoneQueue.getTuples().entrySet()) { + final Tuple update = entry.getKey(); + final int count = entry.getValue(); + assert count > 0; + for (int i = 0; i < count; i++) { + this.receiver.update(Direction.INSERT, update, Timestamp.ZERO); + } + } + this.deliveringMonotone = false; + swapAndClearMonotone(); + } else { + throw new IllegalArgumentException("Unsupported message kind " + kind); + } + } + + @Override + public String toString() { + return "US_MBOX (" + this.receiver + ") " + this.getActiveMonotoneQueue() + " " + + this.getActiveAntiMonotoneQueue(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java new file mode 100644 index 00000000..bf3b8e14 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/mailbox/timely/TimelyMailbox.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.network.mailbox.timely; + +import java.util.Map; +import java.util.TreeMap; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.MessageSelector; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +public class TimelyMailbox implements Mailbox { + + protected TreeMap> queue; + protected final Receiver receiver; + protected final ReteContainer container; + protected CommunicationGroup group; + protected boolean fallThrough; + + public TimelyMailbox(final Receiver receiver, final ReteContainer container) { + this.receiver = receiver; + this.container = container; + this.queue = CollectionsFactory.createTreeMap(); + } + + protected TreeMap> getActiveQueue() { + return this.queue; + } + + @Override + public boolean isEmpty() { + return getActiveQueue().isEmpty(); + } + + @Override + public void postMessage(final Direction direction, final Tuple update, final Timestamp timestamp) { + final TreeMap> activeQueue = getActiveQueue(); + + Map tupleMap = activeQueue.get(timestamp); + final boolean wasEmpty = tupleMap == null; + boolean significantChange = false; + + if (tupleMap == null) { + tupleMap = CollectionsFactory.createMap(); + activeQueue.put(timestamp, tupleMap); + significantChange = true; + } + + Integer count = tupleMap.get(update); + if (count == null) { + count = 0; + significantChange = true; + } + + if (direction == Direction.DELETE) { + count--; + } else { + count++; + } + + if (count == 0) { + tupleMap.remove(update); + if (tupleMap.isEmpty()) { + activeQueue.remove(timestamp); + } + significantChange = true; + } else { + tupleMap.put(update, count); + } + + if (significantChange) { + if (wasEmpty) { + this.group.notifyHasMessage(this, timestamp); + } else if (tupleMap.isEmpty()) { + final Timestamp resumableTimestamp = (this.receiver instanceof ResumableNode) + ? ((ResumableNode) this.receiver).getResumableTimestamp() + : null; + // check if there is folding left to do before unsubscribing just based on the message queue being empty + if (resumableTimestamp == null || resumableTimestamp.compareTo(timestamp) != 0) { + this.group.notifyLostAllMessages(this, timestamp); + } + } + } + } + + @Override + public void deliverAll(final MessageSelector selector) { + if (selector instanceof Timestamp) { + final Timestamp timestamp = (Timestamp) selector; + // REMOVE the tuples associated with the selector, dont just query them + final Map tupleMap = this.queue.remove(timestamp); + + // tupleMap may be empty if we only have lazy folding to do + if (tupleMap != null) { + this.receiver.batchUpdate(tupleMap.entrySet(), timestamp); + } + + if (this.container.getTimelyConfiguration() + .getTimelineRepresentation() == TimelineRepresentation.FAITHFUL) { + // (1) either normal delivery, which ended up being a lazy folding state + // (2) and/or lazy folding needs to be resumed + if (this.receiver instanceof ResumableNode) { + ((ResumableNode) this.receiver).resumeAt(timestamp); + } + } + } else { + throw new IllegalArgumentException("Unsupported message selector " + selector); + } + } + + @Override + public String toString() { + return "DDF_MBOX (" + this.receiver + ") " + this.getActiveQueue(); + } + + @Override + public Receiver getReceiver() { + return this.receiver; + } + + @Override + public void clear() { + this.queue.clear(); + } + + @Override + public CommunicationGroup getCurrentGroup() { + return this.group; + } + + @Override + public void setCurrentGroup(final CommunicationGroup group) { + this.group = group; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java new file mode 100644 index 00000000..2fed3225 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/Address.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.remote; + +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; + +/** + * Remote identifier of a node of type T. + * + * @author Gabor Bergmann + * + */ +public class Address { + ReteContainer container; + Long nodeId; + /** + * Feel free to leave null e.g. if node is in a separate JVM. + */ + T nodeCache; + + /** + * Address of local node (use only for containers in the same VM!) + */ + public static Address of(N node) { + return new Address(node); + } + + /** + * General constructor. + * + * @param container + * @param nodeId + */ + public Address(ReteContainer container, Long nodeId) { + super(); + this.container = container; + this.nodeId = nodeId; + } + + /** + * Local-only constructor. (use only for containers in the same VM!) + * + * @param node + * the node to address + */ + public Address(T node) { + super(); + this.nodeCache = node; + this.container = node.getContainer(); + this.nodeId = node.getNodeId(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((container == null) ? 0 : container.hashCode()); + result = prime * result + ((nodeId == null) ? 0 : nodeId.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof Address)) + return false; + final Address other = (Address) obj; + if (container == null) { + if (other.container != null) + return false; + } else if (!container.equals(other.container)) + return false; + if (nodeId == null) { + if (other.nodeId != null) + return false; + } else if (!nodeId.equals(other.nodeId)) + return false; + return true; + } + + public ReteContainer getContainer() { + return container; + } + + public void setContainer(ReteContainer container) { + this.container = container; + } + + public Long getNodeId() { + return nodeId; + } + + public void setNodeId(Long nodeId) { + this.nodeId = nodeId; + } + + public T getNodeCache() { + return nodeCache; + } + + public void setNodeCache(T nodeCache) { + this.nodeCache = nodeCache; + } + + @Override + public String toString() { + if (nodeCache == null) + return "A(" + nodeId + " @ " + container + ")"; + else + return "A(" + nodeCache + ")"; + + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java new file mode 100644 index 00000000..f7d267af --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteReceiver.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.remote; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.single.SingleInputNode; + +/** + * This node delivers updates to a remote recipient; no updates are propagated further in this network. + * + * @author Gabor Bergmann + * + */ +public class RemoteReceiver extends SingleInputNode { + + List> targets; + + public RemoteReceiver(ReteContainer reteContainer) { + super(reteContainer); + targets = new ArrayList>(); + } + + public void addTarget(Address target) { + targets.add(target); + } + + @Override + public void pullInto(Collection collector, boolean flush) { + propagatePullInto(collector, flush); + } + + @Override + public void pullIntoWithTimeline(Map> collector, boolean flush) { + throw new UnsupportedOperationException(); + } + + public Collection remotePull(boolean flush) { + return reteContainer.pullContents(this, flush); + } + + public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { + for (Address ad : targets) + reteContainer.sendUpdateToRemoteAddress(ad, direction, updateElement); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java new file mode 100644 index 00000000..cbe4d177 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/remote/RemoteSupplier.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.remote; + +import java.util.Collection; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.single.SingleInputNode; + +/** + * This node receives updates from a remote supplier; no local updates are expected. + * + * @author Gabor Bergmann + * + */ +public class RemoteSupplier extends SingleInputNode { + + RemoteReceiver counterpart; + + public RemoteSupplier(ReteContainer reteContainer, RemoteReceiver counterpart) { + super(reteContainer); + this.counterpart = counterpart; + counterpart.addTarget(reteContainer.makeAddress(this)); + } + + @Override + public void pullInto(Collection collector, boolean flush) { + Collection pulled = counterpart.remotePull(flush); + collector.addAll(pulled); + } + + @Override + public void pullIntoWithTimeline(Map> collector, boolean flush) { + throw new UnsupportedOperationException(); + } + + @Override + public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { + propagateUpdate(direction, updateElement, timestamp); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java new file mode 100644 index 00000000..e92ce63f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/AbstractUniquenessEnforcerNode.java @@ -0,0 +1,138 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.single; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; +import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer.ListenerSubscription; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.StandardNode; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.Tunnel; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; +import tools.refinery.viatra.runtime.rete.util.Options; + +/** + * Ensures that no identical copies get to the output. Only one replica of each pattern substitution may traverse this + * node. There are both timeless and timely implementations. + * + * @author Gabor Bergmann + * @author Tamas Szabo + * @noinstantiate This class is not intended to be instantiated by clients. + * @noextend This class is not intended to be subclassed by clients. + * @since 2.2 + */ +public abstract class AbstractUniquenessEnforcerNode extends StandardNode implements Tunnel { + + protected final Collection parents; + protected ProjectionIndexer memoryNullIndexer; + protected ProjectionIndexer memoryIdentityIndexer; + protected final int tupleWidth; + // MUST BE INSTANTIATED IN THE CONCRETE SUBCLASSES AFTER ALL FIELDS ARE SET + protected Mailbox mailbox; + protected final TupleMask nullMask; + protected final TupleMask identityMask; + protected final List specializedListeners; + + public AbstractUniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth) { + super(reteContainer); + this.parents = new ArrayList(); + this.specializedListeners = new ArrayList(); + this.tupleWidth = tupleWidth; + this.nullMask = TupleMask.linear(0, tupleWidth); + this.identityMask = TupleMask.identity(tupleWidth); + } + + protected abstract Mailbox instantiateMailbox(); + + @Override + public Mailbox getMailbox() { + return this.mailbox; + } + + /** + * @since 2.8 + */ + public abstract Set getTuples(); + + /** + * @since 2.4 + */ + protected void propagate(final Direction direction, final Tuple update, final Timestamp timestamp) { + // See Bug 518434 + // trivial (non-active) indexers must be updated before other listeners + // so that if they are joined against each other, trivial indexers lookups + // will be consistent with their notifications; + // also, their subscriptions must share a single order + for (final ListenerSubscription subscription : specializedListeners) { + subscription.propagate(direction, update, timestamp); + } + propagateUpdate(direction, update, timestamp); + } + + @Override + public ProjectionIndexer constructIndex(final TupleMask mask, final TraceInfo... traces) { + if (Options.employTrivialIndexers) { + if (nullMask.equals(mask)) { + final ProjectionIndexer indexer = getNullIndexer(); + for (final TraceInfo traceInfo : traces) { + indexer.assignTraceInfo(traceInfo); + } + return indexer; + } + if (identityMask.equals(mask)) { + final ProjectionIndexer indexer = getIdentityIndexer(); + for (final TraceInfo traceInfo : traces) { + indexer.assignTraceInfo(traceInfo); + } + return indexer; + } + } + return super.constructIndex(mask, traces); + } + + public abstract ProjectionIndexer getNullIndexer(); + + public abstract ProjectionIndexer getIdentityIndexer(); + + @Override + public void appendParent(final Supplier supplier) { + parents.add(supplier); + } + + @Override + public void removeParent(final Supplier supplier) { + parents.remove(supplier); + } + + @Override + public Collection getParents() { + return parents; + } + + @Override + public void assignTraceInfo(final TraceInfo traceInfo) { + super.assignTraceInfo(traceInfo); + if (traceInfo.propagateFromStandardNodeToSupplierParent()) { + for (final Supplier parent : parents) { + parent.acceptPropagatedTraceInfo(traceInfo); + } + } + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java new file mode 100644 index 00000000..c68036b5 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/CallbackNode.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.single; + +import tools.refinery.viatra.runtime.matchers.backend.IUpdateable; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.rete.misc.SimpleReceiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * @author Bergmann Gabor + * + */ +public class CallbackNode extends SimpleReceiver { + + IUpdateable updateable; + + public CallbackNode(ReteContainer reteContainer, IUpdateable updateable) + { + super(reteContainer); + this.updateable = updateable; + } + + @Override + public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { + updateable.update(updateElement, direction == Direction.INSERT); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java new file mode 100644 index 00000000..eca8bc17 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DefaultProductionNode.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.single; + +import java.util.Iterator; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.rete.network.ProductionNode; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; + +/** + * Default implementation of the Production node, based on UniquenessEnforcerNode + * + * @author Gabor Bergmann + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public class DefaultProductionNode extends UniquenessEnforcerNode implements ProductionNode { + + protected final Map posMapping; + + /** + * @since 1.6 + */ + public DefaultProductionNode(final ReteContainer reteContainer, final Map posMapping, + final boolean deleteRederiveEvaluation) { + this(reteContainer, posMapping, deleteRederiveEvaluation, null, null, null); + } + + /** + * @since 1.6 + */ + public DefaultProductionNode(final ReteContainer reteContainer, final Map posMapping, + final boolean deleteRederiveEvaluation, final TupleMask coreMask, final TupleMask posetMask, + final IPosetComparator posetComparator) { + super(reteContainer, posMapping.size(), deleteRederiveEvaluation, coreMask, posetMask, posetComparator); + this.posMapping = posMapping; + } + + @Override + public Map getPosMapping() { + return posMapping; + } + + @Override + public Iterator iterator() { + return memory.iterator(); + } + + @Override + public void acceptPropagatedTraceInfo(final TraceInfo traceInfo) { + if (traceInfo.propagateToProductionNodeParentAlso()) { + super.acceptPropagatedTraceInfo(traceInfo); + } + } + + @Override + public String toString() { + for (final TraceInfo traceInfo : this.traceInfos) { + if (traceInfo instanceof CompiledQuery) { + final String patternName = ((CompiledQuery) traceInfo).getPatternName(); + return String.format(this.getClass().getName() + "<%s>=%s", patternName, super.toString()); + } + } + return super.toString(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java new file mode 100644 index 00000000..803bab20 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorBucketNode.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.single; + +import java.util.Collection; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * A bucket holds a filtered set of tuples of its parent {@link DiscriminatorDispatcherNode}. + * Exactly those that have the given bucket key at their discrimination column. + * + *

During operation, tuple contents and bucket keys have already been wrapped using {@link IQueryRuntimeContext#wrapElement(Object)} + * + * @author Gabor Bergmann + * @since 1.5 + */ +public class DiscriminatorBucketNode extends SingleInputNode { + + private Object bucketKey; + + /** + * @param bucketKey will be wrapped using {@link IQueryRuntimeContext#wrapElement(Object)} + + */ + public DiscriminatorBucketNode(ReteContainer reteContainer, Object bucketKey) { + super(reteContainer); + this.bucketKey = reteContainer.getNetwork().getEngine().getRuntimeContext().wrapElement(bucketKey); + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + if (parent != null) { + getDispatcher().pullIntoFiltered(collector, bucketKey, flush); + } + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + if (parent != null) { + getDispatcher().pullIntoWithTimestampFiltered(collector, bucketKey, flush); + } + } + + @Override + public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { + propagateUpdate(direction, updateElement, timestamp); + } + + public Object getBucketKey() { + return bucketKey; + } + + @Override + public void appendParent(Supplier supplier) { + if (! (supplier instanceof DiscriminatorDispatcherNode)) + throw new IllegalArgumentException(); + super.appendParent(supplier); + } + + public DiscriminatorDispatcherNode getDispatcher() { + return (DiscriminatorDispatcherNode) parent; + } + + @Override + protected String toStringCore() { + return String.format("%s<%s=='%s'>", + super.toStringCore(), + (getDispatcher() == null) ? "?" : getDispatcher().getDiscriminationColumnIndex(), + bucketKey); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java new file mode 100644 index 00000000..a8e11fcd --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/DiscriminatorDispatcherNode.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.single; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.context.IQueryRuntimeContext; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; +import tools.refinery.viatra.runtime.rete.network.Receiver; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; + +/** + * Node that sends tuples off to different buckets (attached as children of type {@link DiscriminatorBucketNode}), based + * on the value of a given column. + * + *

+ * Tuple contents and bucket keys have already been wrapped using {@link IQueryRuntimeContext#wrapElement(Object)} + * + * @author Gabor Bergmann + * @since 1.5 + */ +public class DiscriminatorDispatcherNode extends SingleInputNode implements NetworkStructureChangeSensitiveNode { + + private int discriminationColumnIndex; + private Map buckets = new HashMap<>(); + private Map bucketMailboxes = new HashMap<>(); + + /** + * @param reteContainer + */ + public DiscriminatorDispatcherNode(ReteContainer reteContainer, int discriminationColumnIndex) { + super(reteContainer); + this.discriminationColumnIndex = discriminationColumnIndex; + } + + @Override + public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { + Object dispatchKey = updateElement.get(discriminationColumnIndex); + Mailbox bucketMailBox = bucketMailboxes.get(dispatchKey); + if (bucketMailBox != null) { + bucketMailBox.postMessage(direction, updateElement, timestamp); + } + } + + public int getDiscriminationColumnIndex() { + return discriminationColumnIndex; + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + propagatePullInto(collector, flush); + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + propagatePullIntoWithTimestamp(collector, flush); + } + + /** + * @since 2.3 + */ + public void pullIntoFiltered(final Collection collector, final Object bucketKey, final boolean flush) { + final ArrayList unfiltered = new ArrayList(); + propagatePullInto(unfiltered, flush); + for (Tuple tuple : unfiltered) { + if (bucketKey.equals(tuple.get(discriminationColumnIndex))) { + collector.add(tuple); + } + } + } + + /** + * @since 2.3 + */ + public void pullIntoWithTimestampFiltered(final Map> collector, final Object bucketKey, + final boolean flush) { + final Map> unfiltered = CollectionsFactory.createMap(); + propagatePullIntoWithTimestamp(unfiltered, flush); + for (final Entry> entry : unfiltered.entrySet()) { + if (bucketKey.equals(entry.getKey().get(discriminationColumnIndex))) { + collector.put(entry.getKey(), entry.getValue()); + } + } + } + + @Override + public void appendChild(Receiver receiver) { + super.appendChild(receiver); + if (receiver instanceof DiscriminatorBucketNode) { + DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver; + Object bucketKey = bucket.getBucketKey(); + DiscriminatorBucketNode old = buckets.put(bucketKey, bucket); + if (old != null) { + throw new IllegalStateException(); + } + bucketMailboxes.put(bucketKey, this.getCommunicationTracker().proxifyMailbox(this, bucket.getMailbox())); + } + } + + /** + * @since 2.2 + */ + public Map getBucketMailboxes() { + return this.bucketMailboxes; + } + + @Override + public void networkStructureChanged() { + bucketMailboxes.clear(); + for (Receiver receiver : children) { + if (receiver instanceof DiscriminatorBucketNode) { + DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver; + Object bucketKey = bucket.getBucketKey(); + bucketMailboxes.put(bucketKey, + this.getCommunicationTracker().proxifyMailbox(this, bucket.getMailbox())); + } + } + } + + @Override + public void removeChild(Receiver receiver) { + super.removeChild(receiver); + if (receiver instanceof DiscriminatorBucketNode) { + DiscriminatorBucketNode bucket = (DiscriminatorBucketNode) receiver; + Object bucketKey = bucket.getBucketKey(); + DiscriminatorBucketNode old = buckets.remove(bucketKey); + if (old != bucket) + throw new IllegalStateException(); + bucketMailboxes.remove(bucketKey); + } + } + + @Override + protected String toStringCore() { + return super.toStringCore() + '<' + discriminationColumnIndex + '>'; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java new file mode 100644 index 00000000..014c2016 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/EqualityFilterNode.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.single; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; + +public class EqualityFilterNode extends FilterNode { + + int[] indices; + int first; + + /** + * @param reteContainer + * @param indices + * indices of the Tuple that should hold equal values + */ + public EqualityFilterNode(ReteContainer reteContainer, int[] indices) { + super(reteContainer); + this.indices = indices; + first = indices[0]; + } + + @Override + public boolean check(Tuple ps) { + Object firstElement = ps.get(first); + for (int i = 1 /* first is omitted */; i < indices.length; i++) { + if (!ps.get(indices[i]).equals(firstElement)) + return false; + } + return true; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java new file mode 100644 index 00000000..f66f1715 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/FilterNode.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.single; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * This node implements a simple filter. A stateless abstract check() predicate determines whether a matching is allowed + * to pass. + * + * @author Gabor Bergmann + * + */ +public abstract class FilterNode extends SingleInputNode { + + public FilterNode(final ReteContainer reteContainer) { + super(reteContainer); + } + + /** + * Abstract filtering predicate. Expected to be stateless. + * + * @param ps + * the matching to be checked. + * @return true if and only if the parameter matching is allowed to pass through this node. + */ + public abstract boolean check(final Tuple ps); + + @Override + public void pullInto(final Collection collector, final boolean flush) { + for (final Tuple ps : this.reteContainer.pullPropagatedContents(this, flush)) { + if (check(ps)) { + collector.add(ps); + } + } + } + + @Override + public void pullIntoWithTimeline(Map> collector, boolean flush) { + for (final Entry> entry : this.reteContainer.pullPropagatedContentsWithTimestamp(this, flush).entrySet()) { + if (check(entry.getKey())) { + collector.put(entry.getKey(), entry.getValue()); + } + } + } + + @Override + public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { + if (check(updateElement)) { + propagateUpdate(direction, updateElement, timestamp); + } + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java new file mode 100644 index 00000000..8dd3e949 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/InequalityFilterNode.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.single; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; + +/** + * This node filters patterns according to equalities and inequalities of elements. The 'subject' element is asserted to + * be different from the elements given by the inequalityMask. + * + * + * @author Gabor Bergmann + * + */ +public class InequalityFilterNode extends FilterNode { + + int subjectIndex; + TupleMask inequalityMask; + + /** + * @param reteContainer + * @param subject + * the index of the element that should be compared. + * @param inequalityMask + * the indices of elements that should be different from the subjectIndex. + */ + public InequalityFilterNode(ReteContainer reteContainer, int subject, TupleMask inequalityMask) { + super(reteContainer); + this.subjectIndex = subject; + this.inequalityMask = inequalityMask; + } + + @Override + public boolean check(Tuple ps) { + Object subject = ps.get(subjectIndex); + for (int ineq : inequalityMask.indices) { + if (subject.equals(ps.get(ineq))) + return false; + } + return true; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java new file mode 100644 index 00000000..6a1e305c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java @@ -0,0 +1,125 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, Istvan Rath and Daniel Varro + * Copyright (c) 2023 The Refinery Authors + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.single; + +import tools.refinery.viatra.runtime.base.itc.alg.representative.RepresentativeElectionAlgorithm; +import tools.refinery.viatra.runtime.base.itc.alg.representative.RepresentativeObserver; +import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.Clearable; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReinitializedNode; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +import java.util.Collection; +import java.util.Map; + +public class RepresentativeElectionNode extends SingleInputNode implements Clearable, RepresentativeObserver, + ReinitializedNode { + private final RepresentativeElectionAlgorithm.Factory algorithmFactory; + private Graph graph; + private RepresentativeElectionAlgorithm algorithm; + + public RepresentativeElectionNode(ReteContainer reteContainer, + RepresentativeElectionAlgorithm.Factory algorithmFactory) { + super(reteContainer); + this.algorithmFactory = algorithmFactory; + graph = new Graph<>(); + algorithm = algorithmFactory.create(graph); + algorithm.setObserver(this); + reteContainer.registerClearable(this); + } + + @Override + public void networkStructureChanged() { + if (reteContainer.isTimelyEvaluation() && reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { + throw new IllegalStateException(this + " cannot be used in recursive differential dataflow evaluation!"); + } + super.networkStructureChanged(); + } + + @Override + public void reinitializeWith(Collection tuples) { + algorithm.dispose(); + graph = new Graph<>(); + for (var tuple : tuples) { + insertEdge(tuple.get(0), tuple.get(1)); + } + algorithm = algorithmFactory.create(graph); + algorithm.setObserver(this); + } + + @Override + public void tupleChanged(Object source, Object representative, Direction direction) { + var tuple = Tuples.staticArityFlatTupleOf(source, representative); + propagateUpdate(direction, tuple, Timestamp.ZERO); + } + + @Override + public void clear() { + algorithm.dispose(); + graph = new Graph<>(); + algorithm = algorithmFactory.create(graph); + } + + @Override + public void update(Direction direction, Tuple updateElement, Timestamp timestamp) { + var source = updateElement.get(0); + var target = updateElement.get(1); + switch (direction) { + case INSERT -> insertEdge(source, target); + case DELETE -> deleteEdge(source, target); + default -> throw new IllegalArgumentException("Unknown direction: " + direction); + } + } + + private void insertEdge(Object source, Object target) { + graph.insertNode(source); + graph.insertNode(target); + graph.insertEdge(source, target); + } + + private void deleteEdge(Object source, Object target) { + graph.deleteEdgeIfExists(source, target); + if (isIsolated(source)) { + graph.deleteNode(source); + } + if (!source.equals(target) && isIsolated(target)) { + graph.deleteNode(target); + } + } + + private boolean isIsolated(Object node) { + return graph.getTargetNodes(node).isEmpty() && graph.getSourceNodes(node).isEmpty(); + } + + @Override + public void pullInto(Collection collector, boolean flush) { + for (var entry : algorithm.getComponents().entrySet()) { + var representative = entry.getKey(); + for (var node : entry.getValue()) { + collector.add(Tuples.staticArityFlatTupleOf(node, representative)); + } + } + } + + @Override + public void pullIntoWithTimeline(Map> collector, boolean flush) { + // Use all zero timestamps because this node cannot be used in recursive groups anyway. + for (var entry : algorithm.getComponents().entrySet()) { + var representative = entry.getKey(); + for (var node : entry.getValue()) { + collector.put(Tuples.staticArityFlatTupleOf(node, representative), Timestamp.INSERT_AT_ZERO_TIMELINE); + } + } + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java new file mode 100644 index 00000000..99fc45b2 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/SingleInputNode.java @@ -0,0 +1,126 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.single; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.StandardNode; +import tools.refinery.viatra.runtime.rete.network.Supplier; +import tools.refinery.viatra.runtime.rete.network.Tunnel; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationTracker; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; + +/** + * @author Gabor Bergmann + * + */ +public abstract class SingleInputNode extends StandardNode implements Tunnel { + + protected Supplier parent; + /** + * @since 1.6 + */ + protected Mailbox mailbox; + + public SingleInputNode(ReteContainer reteContainer) { + super(reteContainer); + mailbox = instantiateMailbox(); + reteContainer.registerClearable(mailbox); + parent = null; + } + + /** + * Instantiates the {@link Mailbox} of this receiver. + * Subclasses may override this method to provide their own mailbox implementation. + * + * @return the mailbox + * @since 2.0 + */ + protected Mailbox instantiateMailbox() { + if (this.reteContainer.isTimelyEvaluation()) { + return new TimelyMailbox(this, this.reteContainer); + } else { + return new BehaviorChangingMailbox(this, this.reteContainer); + } + } + + @Override + public CommunicationTracker getCommunicationTracker() { + return this.reteContainer.getCommunicationTracker(); + } + + @Override + public Mailbox getMailbox() { + return this.mailbox; + } + + @Override + public void appendParent(Supplier supplier) { + if (parent == null) + parent = supplier; + else + throw new UnsupportedOperationException("Illegal RETE edge: " + this + " already has a parent (" + parent + + ") and cannot connect to additional parent (" + supplier + + ") as it is not a Uniqueness Enforcer Node. "); + } + + @Override + public void removeParent(Supplier supplier) { + if (parent == supplier) + parent = null; + else + throw new IllegalArgumentException("Illegal RETE edge removal: the parent of " + this + " is not " + + supplier); + } + + /** + * To be called by derived classes and ReteContainer. + */ + public void propagatePullInto(final Collection collector, final boolean flush) { + if (parent != null) { + parent.pullInto(collector, flush); + } + } + + /** + * To be called by derived classes and ReteContainer. + */ + public void propagatePullIntoWithTimestamp(final Map> collector, final boolean flush) { + if (parent != null) { + parent.pullIntoWithTimeline(collector, flush); + } + } + + @Override + public Collection getParents() { + if (parent == null) + return Collections.emptySet(); + else + return Collections.singleton(parent); + } + + @Override + public void assignTraceInfo(TraceInfo traceInfo) { + super.assignTraceInfo(traceInfo); + if (traceInfo.propagateFromStandardNodeToSupplierParent()) + if (parent != null) + parent.acceptPropagatedTraceInfo(traceInfo); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java new file mode 100644 index 00000000..82640948 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyProductionNode.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.single; + +import java.util.Iterator; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.rete.network.ProductionNode; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.traceability.CompiledQuery; +import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; +/** + * Differential dataflow implementation of the Production node, based on {@link TimelyUniquenessEnforcerNode}. + * + * @author Tamas Szabo + * @noinstantiate This class is not intended to be instantiated by clients. + * @since 2.3 + */ +public class TimelyProductionNode extends TimelyUniquenessEnforcerNode implements ProductionNode { + + protected final Map posMapping; + + public TimelyProductionNode(final ReteContainer reteContainer, final Map posMapping) { + super(reteContainer, posMapping.size()); + this.posMapping = posMapping; + } + + @Override + public Map getPosMapping() { + return this.posMapping; + } + + @Override + public Iterator iterator() { + return this.memory.keySet().iterator(); + } + + @Override + public void acceptPropagatedTraceInfo(final TraceInfo traceInfo) { + if (traceInfo.propagateToProductionNodeParentAlso()) { + super.acceptPropagatedTraceInfo(traceInfo); + } + } + + @Override + public String toString() { + for (final TraceInfo traceInfo : this.traceInfos) { + if (traceInfo instanceof CompiledQuery) { + final String patternName = ((CompiledQuery) traceInfo).getPatternName(); + return String.format(this.getClass().getName() + "<%s>=%s", patternName, super.toString()); + } + } + return super.toString(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java new file mode 100644 index 00000000..4c4b4fc0 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TimelyUniquenessEnforcerNode.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.single; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; +import tools.refinery.viatra.runtime.rete.index.timely.TimelyMemoryIdentityIndexer; +import tools.refinery.viatra.runtime.rete.index.timely.TimelyMemoryNullIndexer; +import tools.refinery.viatra.runtime.rete.matcher.TimelyConfiguration.TimelineRepresentation; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timely.TimelyMailbox; + +/** + * Timely uniqueness enforcer node implementation. + * + * @author Tamas Szabo + * @noinstantiate This class is not intended to be instantiated by clients. + * @noextend This class is not intended to be subclassed by clients. + * @since 2.4 + */ +public class TimelyUniquenessEnforcerNode extends AbstractUniquenessEnforcerNode implements ResumableNode { + + protected final TimelyMemory memory; + /** + * @since 2.4 + */ + protected CommunicationGroup group; + + public TimelyUniquenessEnforcerNode(final ReteContainer container, final int tupleWidth) { + super(container, tupleWidth); + this.memory = new TimelyMemory( + container.getTimelyConfiguration().getTimelineRepresentation() == TimelineRepresentation.FAITHFUL); + container.registerClearable(this.memory); + this.mailbox = instantiateMailbox(); + container.registerClearable(this.mailbox); + } + + protected Mailbox instantiateMailbox() { + return new TimelyMailbox(this, this.reteContainer); + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + for (final Tuple tuple : this.memory.getTuplesAtInfinity()) { + collector.add(tuple); + } + } + + @Override + public CommunicationGroup getCurrentGroup() { + return this.group; + } + + @Override + public void setCurrentGroup(final CommunicationGroup group) { + this.group = group; + } + + @Override + public Set getTuples() { + return this.memory.getTuplesAtInfinity(); + } + + /** + * @since 2.4 + */ + @Override + public Timestamp getResumableTimestamp() { + return this.memory.getResumableTimestamp(); + } + + /** + * @since 2.4 + */ + @Override + public void resumeAt(final Timestamp timestamp) { + final Map> diffMap = this.memory.resumeAt(timestamp); + for (final Entry> entry : diffMap.entrySet()) { + for (final Signed signed : entry.getValue()) { + propagate(signed.getDirection(), entry.getKey(), signed.getPayload()); + } + } + final Timestamp nextTimestamp = this.memory.getResumableTimestamp(); + if (nextTimestamp != null) { + this.group.notifyHasMessage(this.mailbox, nextTimestamp); + } + } + + @Override + public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { + Diff resultDiff = null; + if (direction == Direction.INSERT) { + resultDiff = this.memory.put(update, timestamp); + } else { + try { + resultDiff = this.memory.remove(update, timestamp); + } catch (final IllegalStateException e) { + issueError("[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in " + + this.getClass().getName() + " " + this + " for pattern(s) " + + getTraceInfoPatternsEnumerated(), e); + // diff will remain unset in case of the exception, it is time to return + return; + } + } + + for (final Signed signed : resultDiff) { + propagate(signed.getDirection(), update, signed.getPayload()); + } + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + collector.putAll(this.memory.asMap()); + } + + @Override + public ProjectionIndexer getNullIndexer() { + if (this.memoryNullIndexer == null) { + this.memoryNullIndexer = new TimelyMemoryNullIndexer(this.reteContainer, this.tupleWidth, this.memory, this, + this, this.specializedListeners); + this.getCommunicationTracker().registerDependency(this, this.memoryNullIndexer); + } + return this.memoryNullIndexer; + } + + @Override + public ProjectionIndexer getIdentityIndexer() { + if (this.memoryIdentityIndexer == null) { + this.memoryIdentityIndexer = new TimelyMemoryIdentityIndexer(this.reteContainer, this.tupleWidth, + this.memory, this, this, this.specializedListeners); + this.getCommunicationTracker().registerDependency(this, this.memoryIdentityIndexer); + } + return this.memoryIdentityIndexer; + } + + @Override + public void networkStructureChanged() { + super.networkStructureChanged(); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java new file mode 100644 index 00000000..24750656 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransformerNode.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.single; + +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +public abstract class TransformerNode extends SingleInputNode { + + public TransformerNode(final ReteContainer reteContainer) { + super(reteContainer); + } + + protected abstract Tuple transform(final Tuple input); + + @Override + public void pullInto(final Collection collector, final boolean flush) { + for (Tuple ps : reteContainer.pullPropagatedContents(this, flush)) { + collector.add(transform(ps)); + } + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + for (final Entry> entry : reteContainer.pullPropagatedContentsWithTimestamp(this, flush).entrySet()) { + collector.put(transform(entry.getKey()), entry.getValue()); + } + } + + @Override + public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { + propagateUpdate(direction, transform(updateElement), timestamp); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java new file mode 100644 index 00000000..eeead31b --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java @@ -0,0 +1,147 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Gabor Bergmann, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.single; + +import tools.refinery.viatra.runtime.base.itc.alg.incscc.IncSCCAlg; +import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple; +import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.Clearable; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.NetworkStructureChangeSensitiveNode; +import tools.refinery.viatra.runtime.rete.network.ReinitializedNode; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +import java.util.Collection; +import java.util.Map; + +/** + * This class represents a transitive closure node in the Rete net. + *

+ * This node must not be used in recursive {@link CommunicationGroup}s. + * + * @author Gabor Bergmann + * + */ +public class TransitiveClosureNode extends SingleInputNode + implements Clearable, ITcObserver, NetworkStructureChangeSensitiveNode, ReinitializedNode { + + private Graph graphDataSource; + private ITcDataSource transitiveClosureAlgorithm; + + /** + * Create a new transitive closure rete node. + * + * Client may optionally call {@link #reinitializeWith(Collection)} before using the node, instead of inserting the + * initial set of tuples one by one. + * + * @param reteContainer + * the rete container of the node + */ + public TransitiveClosureNode(ReteContainer reteContainer) { + super(reteContainer); + graphDataSource = new Graph(); + transitiveClosureAlgorithm = new IncSCCAlg(graphDataSource); + transitiveClosureAlgorithm.attachObserver(this); + reteContainer.registerClearable(this); + } + + @Override + public void networkStructureChanged() { + if (this.reteContainer.isTimelyEvaluation() && this.reteContainer.getCommunicationTracker().isInRecursiveGroup(this)) { + throw new IllegalStateException(this.toString() + " cannot be used in recursive differential dataflow evaluation!"); + } + super.networkStructureChanged(); + } + + /** + * Initializes the graph data source with the given collection of tuples. + * + * @param tuples + * the initial collection of tuples + */ + @Override + public void reinitializeWith(Collection tuples) { + clear(); + + for (tools.refinery.viatra.runtime.matchers.tuple.Tuple t : tuples) { + graphDataSource.insertNode(t.get(0)); + graphDataSource.insertNode(t.get(1)); + graphDataSource.insertEdge(t.get(0), t.get(1)); + } + transitiveClosureAlgorithm.attachObserver(this); + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + for (final Tuple tuple : ((IncSCCAlg) transitiveClosureAlgorithm).getTcRelation()) { + collector.add(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget())); + } + } + + @Override + public void pullIntoWithTimeline( + final Map> collector, + final boolean flush) { + // use all zero timestamps because this node cannot be used in recursive groups anyway + for (final Tuple tuple : ((IncSCCAlg) transitiveClosureAlgorithm).getTcRelation()) { + collector.put(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget()), Timestamp.INSERT_AT_ZERO_TIMELINE); + } + } + + @Override + public void update(Direction direction, tools.refinery.viatra.runtime.matchers.tuple.Tuple updateElement, + Timestamp timestamp) { + if (updateElement.getSize() == 2) { + Object source = updateElement.get(0); + Object target = updateElement.get(1); + + if (direction == Direction.INSERT) { + graphDataSource.insertNode(source); + graphDataSource.insertNode(target); + graphDataSource.insertEdge(source, target); + } + if (direction == Direction.DELETE) { + graphDataSource.deleteEdgeIfExists(source, target); + + if (((IncSCCAlg) transitiveClosureAlgorithm).isIsolated(source)) { + graphDataSource.deleteNode(source); + } + if (!source.equals(target) && ((IncSCCAlg) transitiveClosureAlgorithm).isIsolated(target)) { + graphDataSource.deleteNode(target); + } + } + } + } + + @Override + public void clear() { + transitiveClosureAlgorithm.dispose(); + graphDataSource = new Graph(); + transitiveClosureAlgorithm = new IncSCCAlg(graphDataSource); + } + + @Override + public void tupleInserted(Object source, Object target) { + tools.refinery.viatra.runtime.matchers.tuple.Tuple tuple = Tuples.staticArityFlatTupleOf(source, target); + propagateUpdate(Direction.INSERT, tuple, Timestamp.ZERO); + } + + @Override + public void tupleDeleted(Object source, Object target) { + tools.refinery.viatra.runtime.matchers.tuple.Tuple tuple = Tuples.staticArityFlatTupleOf(source, target); + propagateUpdate(Direction.DELETE, tuple, Timestamp.ZERO); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java new file mode 100644 index 00000000..6c21a966 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransparentNode.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.single; + +import java.util.Collection; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; + +/** + * Simply propagates everything. Might be used to join or fork. + * + * @author Gabor Bergmann + */ +public class TransparentNode extends SingleInputNode { + + public TransparentNode(final ReteContainer reteContainer) { + super(reteContainer); + } + + @Override + public void update(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { + propagateUpdate(direction, updateElement, timestamp); + + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + propagatePullInto(collector, flush); + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + propagatePullIntoWithTimestamp(collector, flush); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java new file mode 100644 index 00000000..8a72138c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TrimmerNode.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.single; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; + +/** + * Trims the matchings as specified by a mask. + * + * @author Gabor Bergmann + * + */ +public class TrimmerNode extends TransformerNode { + + protected TupleMask mask; + + /** + * @param reteContainer + * @param mask + * The mask used to trim substitutions. + */ + public TrimmerNode(ReteContainer reteContainer, TupleMask mask) { + super(reteContainer); + this.mask = mask; + } + + public TrimmerNode(ReteContainer reteContainer) { + super(reteContainer); + this.mask = null; + } + + /** + * @return the mask + */ + public TupleMask getMask() { + return mask; + } + + /** + * @param mask + * the mask to set + */ + public void setMask(TupleMask mask) { + this.mask = mask; + } + + @Override + protected Tuple transform(Tuple input) { + return mask.transform(input); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java new file mode 100644 index 00000000..5bfde248 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/UniquenessEnforcerNode.java @@ -0,0 +1,321 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann, Tamas Szabo and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.single; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.IMultiset; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.rete.index.MemoryIdentityIndexer; +import tools.refinery.viatra.runtime.rete.index.MemoryNullIndexer; +import tools.refinery.viatra.runtime.rete.index.ProjectionIndexer; +import tools.refinery.viatra.runtime.rete.network.PosetAwareReceiver; +import tools.refinery.viatra.runtime.rete.network.RederivableNode; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; +import tools.refinery.viatra.runtime.rete.network.communication.CommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; +import tools.refinery.viatra.runtime.rete.network.communication.timeless.RecursiveCommunicationGroup; +import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.BehaviorChangingMailbox; +import tools.refinery.viatra.runtime.rete.network.mailbox.timeless.PosetAwareMailbox; + +/** + * Timeless uniqueness enforcer node implementation. + *

+ * The node is capable of operating in the delete and re-derive mode. In this mode, it is also possible to equip the + * node with an {@link IPosetComparator} to identify monotone changes; thus, ensuring that a fix-point can be reached + * during the evaluation. + * + * @author Gabor Bergmann + * @author Tamas Szabo + * @noinstantiate This class is not intended to be instantiated by clients. + * @noextend This class is not intended to be subclassed by clients. + */ +public class UniquenessEnforcerNode extends AbstractUniquenessEnforcerNode + implements RederivableNode, PosetAwareReceiver { + + protected IMultiset memory; + /** + * @since 1.6 + */ + protected IMultiset rederivableMemory; + /** + * @since 1.6 + */ + protected boolean deleteRederiveEvaluation; + + /** + * @since 1.7 + */ + protected CommunicationGroup currentGroup; + + public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth) { + this(reteContainer, tupleWidth, false); + } + + /** + * OPTIONAL ELEMENT - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE + * + * @since 1.6 + */ + protected final TupleMask coreMask; + /** + * OPTIONAL ELEMENTS - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE + * + * @since 1.6 + */ + protected final TupleMask posetMask; + /** + * OPTIONAL ELEMENTS - ONLY PRESENT IF MONOTONICITY INFO WAS AVAILABLE + * + * @since 1.6 + */ + protected final IPosetComparator posetComparator; + + /** + * @since 1.6 + */ + public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth, + final boolean deleteRederiveEvaluation) { + this(reteContainer, tupleWidth, deleteRederiveEvaluation, null, null, null); + } + + /** + * @since 1.6 + */ + public UniquenessEnforcerNode(final ReteContainer reteContainer, final int tupleWidth, + final boolean deleteRederiveEvaluation, final TupleMask coreMask, final TupleMask posetMask, + final IPosetComparator posetComparator) { + super(reteContainer, tupleWidth); + this.memory = CollectionsFactory.createMultiset(); + this.rederivableMemory = CollectionsFactory.createMultiset(); + reteContainer.registerClearable(this.memory); + reteContainer.registerClearable(this.rederivableMemory); + this.deleteRederiveEvaluation = deleteRederiveEvaluation; + this.coreMask = coreMask; + this.posetMask = posetMask; + this.posetComparator = posetComparator; + this.mailbox = instantiateMailbox(); + reteContainer.registerClearable(this.mailbox); + } + + @Override + public void pullInto(final Collection collector, final boolean flush) { + for (final Tuple tuple : this.memory.distinctValues()) { + collector.add(tuple); + } + } + + /** + * @since 2.8 + */ + @Override + public Set getTuples() { + return this.memory.distinctValues(); + } + + @Override + public boolean isInDRedMode() { + return this.deleteRederiveEvaluation; + } + + @Override + public TupleMask getCoreMask() { + return coreMask; + } + + @Override + public TupleMask getPosetMask() { + return posetMask; + } + + @Override + public IPosetComparator getPosetComparator() { + return posetComparator; + } + + @Override + public void pullIntoWithTimeline(final Map> collector, final boolean flush) { + throw new UnsupportedOperationException("Use the timely version of this node!"); + } + + /** + * @since 2.0 + */ + protected Mailbox instantiateMailbox() { + if (coreMask != null && posetMask != null && posetComparator != null) { + return new PosetAwareMailbox(this, this.reteContainer); + } else { + return new BehaviorChangingMailbox(this, this.reteContainer); + } + } + + @Override + public void update(final Direction direction, final Tuple update, final Timestamp timestamp) { + updateWithPosetInfo(direction, update, false); + } + + @Override + public void updateWithPosetInfo(final Direction direction, final Tuple update, final boolean monotone) { + if (this.deleteRederiveEvaluation) { + if (updateWithDeleteAndRederive(direction, update, monotone)) { + propagate(direction, update, Timestamp.ZERO); + } + } else { + if (updateDefault(direction, update)) { + propagate(direction, update, Timestamp.ZERO); + } + } + } + + /** + * @since 2.4 + */ + protected boolean updateWithDeleteAndRederive(final Direction direction, final Tuple update, + final boolean monotone) { + boolean propagate = false; + + final int memoryCount = memory.getCount(update); + final int rederivableCount = rederivableMemory.getCount(update); + + if (direction == Direction.INSERT) { + // INSERT + if (rederivableCount != 0) { + // the tuple is in the re-derivable memory + rederivableMemory.addOne(update); + if (rederivableMemory.isEmpty()) { + // there is nothing left to be re-derived + // this can happen if the INSERT cancelled out a DELETE + ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); + } + } else { + // the tuple is in the main memory + propagate = memory.addOne(update); + } + } else { + // DELETE + if (rederivableCount != 0) { + // the tuple is in the re-derivable memory + if (memoryCount != 0) { + issueError("[INTERNAL ERROR] Inconsistent state for " + update + + " because it is present both in the main and re-derivable memory in the UniquenessEnforcerNode " + + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(), null); + } + + try { + rederivableMemory.removeOne(update); + } catch (final IllegalStateException ex) { + issueError( + "[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in UniquenessEnforcer " + + this + " for pattern(s) " + getTraceInfoPatternsEnumerated(), + ex); + } + if (rederivableMemory.isEmpty()) { + // there is nothing left to be re-derived + ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); + } + } else { + // the tuple is in the main memory + if (monotone) { + propagate = memory.removeOne(update); + } else { + final int count = memoryCount - 1; + if (count > 0) { + if (rederivableMemory.isEmpty()) { + // there is now something to be re-derived + ((RecursiveCommunicationGroup) currentGroup).addRederivable(this); + } + rederivableMemory.addPositive(update, count); + } + memory.clearAllOf(update); + propagate = true; + } + } + } + + return propagate; + } + + /** + * @since 2.4 + */ + protected boolean updateDefault(final Direction direction, final Tuple update) { + boolean propagate = false; + if (direction == Direction.INSERT) { + // INSERT + propagate = memory.addOne(update); + } else { + // DELETE + try { + propagate = memory.removeOne(update); + } catch (final IllegalStateException ex) { + propagate = false; + issueError("[INTERNAL ERROR] Duplicate deletion of " + update + " was detected in " + + this.getClass().getName() + " " + this + " for pattern(s) " + + getTraceInfoPatternsEnumerated(), ex); + } + } + return propagate; + } + + /** + * @since 1.6 + */ + @Override + public void rederiveOne() { + final Tuple update = rederivableMemory.iterator().next(); + final int count = rederivableMemory.getCount(update); + rederivableMemory.clearAllOf(update); + memory.addPositive(update, count); + // if there is no other re-derivable tuple, then unregister the node itself + if (this.rederivableMemory.isEmpty()) { + ((RecursiveCommunicationGroup) currentGroup).removeRederivable(this); + } + propagate(Direction.INSERT, update, Timestamp.ZERO); + } + + @Override + public ProjectionIndexer getNullIndexer() { + if (this.memoryNullIndexer == null) { + this.memoryNullIndexer = new MemoryNullIndexer(this.reteContainer, this.tupleWidth, + this.memory.distinctValues(), this, this, this.specializedListeners); + this.getCommunicationTracker().registerDependency(this, this.memoryNullIndexer); + } + return this.memoryNullIndexer; + } + + @Override + public ProjectionIndexer getIdentityIndexer() { + if (this.memoryIdentityIndexer == null) { + this.memoryIdentityIndexer = new MemoryIdentityIndexer(this.reteContainer, this.tupleWidth, + this.memory.distinctValues(), this, this, this.specializedListeners); + this.getCommunicationTracker().registerDependency(this, this.memoryIdentityIndexer); + } + return this.memoryIdentityIndexer; + } + + @Override + public CommunicationGroup getCurrentGroup() { + return currentGroup; + } + + @Override + public void setCurrentGroup(final CommunicationGroup currentGroup) { + this.currentGroup = currentGroup; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java new file mode 100644 index 00000000..c641bf6e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/ValueBinderFilterNode.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.single; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.rete.network.ReteContainer; + +/** + * A filter node that keeps only those tuples that contain a certain value at a certain position. + * + * @author Bergmann Gabor + * + */ +public class ValueBinderFilterNode extends FilterNode { + + int bindingIndex; + Object bindingValue; + + /** + * @param reteContainer + * @param bindingIndex + * the position in the tuple that should be bound + * @param bindingValue + * the value to which the tuple has to be bound + */ + public ValueBinderFilterNode(ReteContainer reteContainer, int bindingIndex, Object bindingValue) { + super(reteContainer); + this.bindingIndex = bindingIndex; + this.bindingValue = bindingValue; + } + + @Override + public boolean check(Tuple ps) { + return bindingValue.equals(ps.get(bindingIndex)); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java new file mode 100644 index 00000000..2055dfe8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ActiveNodeConflictTrace.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.traceability; + +import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; + +public class ActiveNodeConflictTrace extends RecipeTraceInfo { // TODO implement PatternTraceInfo + RecipeTraceInfo inactiveRecipeTrace; + public ActiveNodeConflictTrace(ReteNodeRecipe recipe, + RecipeTraceInfo parentRecipeTrace, + RecipeTraceInfo inactiveRecipeTrace) { + super(recipe, parentRecipeTrace); + this.inactiveRecipeTrace = inactiveRecipeTrace; + } + public RecipeTraceInfo getInactiveRecipeTrace() { + return inactiveRecipeTrace; + } +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java new file mode 100644 index 00000000..b8c793c5 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledQuery.java @@ -0,0 +1,54 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.traceability; + +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; + +/** + * Indicates that recipe expresses the finished match set of a query. + * @author Bergmann Gabor + * @noinstantiate This class is not intended to be instantiated by clients. + */ +public class CompiledQuery extends RecipeTraceInfo implements + PatternTraceInfo { + + private PQuery query; + private final Map parentRecipeTracesPerBody; + + /** + * @since 1.6 + */ + public CompiledQuery(ReteNodeRecipe recipe, + Map parentRecipeTraces, + PQuery query) { + super(recipe, parentRecipeTraces.values()); + parentRecipeTracesPerBody = parentRecipeTraces; + this.query = query; + } + public PQuery getQuery() { + return query; + } + + @Override + public String getPatternName() { + return query.getFullyQualifiedName(); + } + + /** + * @since 1.6 + */ + public Map getParentRecipeTracesPerBody() { + return parentRecipeTracesPerBody; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java new file mode 100644 index 00000000..572e943c --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/CompiledSubPlan.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.traceability; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; +import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; + +/** + * A trace marker associating a Rete recipe with a query SubPlan. + * + *

The Rete node represented by the recipe is equivalent to the SubPlan. + *

Invariant: each variable has at most one index associated with it in the tuple, i.e. no duplicates. + */ +public class CompiledSubPlan extends PlanningTrace { + + public CompiledSubPlan(SubPlan subPlan, List variablesTuple, + ReteNodeRecipe recipe, + Collection parentRecipeTraces) { + super(subPlan, variablesTuple, recipe, parentRecipeTraces); + + // Make sure that each variable occurs only once + Set variablesSet = new HashSet(variablesTuple); + Preconditions.checkState(variablesSet.size() == variablesTuple.size(), + () -> String.format( + "Illegal column duplication (%s) while the query plan %s was compiled into a Rete Recipe %s", + variablesTuple.stream().map(PVariable::getName).collect(Collectors.joining(",")), + subPlan.toShortString(), recipe)); + } + + public CompiledSubPlan(SubPlan subPlan, List variablesTuple, + ReteNodeRecipe recipe, + RecipeTraceInfo... parentRecipeTraces) { + this(subPlan, variablesTuple, recipe, Arrays.asList(parentRecipeTraces)); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java new file mode 100644 index 00000000..8da1e314 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/ParameterProjectionTrace.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.traceability; + +import java.util.Arrays; +import java.util.Collection; + +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; + +/** + * The recipe projects the finished results of a {@link PBody} onto the list of parameters. + * @author Bergmann Gabor + * + */ +public class ParameterProjectionTrace extends RecipeTraceInfo implements PatternTraceInfo { + + public ParameterProjectionTrace(PBody body, ReteNodeRecipe recipe, + RecipeTraceInfo... parentRecipeTraces) { + this(body, recipe, Arrays.asList(parentRecipeTraces)); + } + + public ParameterProjectionTrace(PBody body, ReteNodeRecipe recipe, + Collection parentRecipeTraces) { + super(recipe, parentRecipeTraces); + this.body = body; + } + + PBody body; + + @Override + public String getPatternName() { + return body.getPattern().getFullyQualifiedName(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java new file mode 100644 index 00000000..fb7ef062 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PatternTraceInfo.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.traceability; + +/** + * One kind of trace marker that merely establishes the pattern for which the node was built. + * @author Bergmann Gabor + */ +public interface PatternTraceInfo extends TraceInfo { + String getPatternName(); +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java new file mode 100644 index 00000000..c1cc3a69 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/PlanningTrace.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.traceability; + +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; + +/** + * A trace marker associating a Rete recipe with a query SubPlan. + * + *

The recipe may be an auxiliary node; + * see {@link CompiledSubPlan} if it represents the entire SubPlan instead. + */ +public class PlanningTrace extends RecipeTraceInfo implements PatternTraceInfo { + + protected SubPlan subPlan; + protected List variablesTuple; + protected Map posMapping; + + public PlanningTrace(SubPlan subPlan, List variablesTuple, + ReteNodeRecipe recipe, + Collection parentRecipeTraces) { + super(recipe, parentRecipeTraces); + this.subPlan = subPlan; + this.variablesTuple = variablesTuple; + + this.posMapping = new HashMap(); + for (int i = 0; i < variablesTuple.size(); ++i) + posMapping.put(variablesTuple.get(i), i); + } + + public PlanningTrace(SubPlan subPlan, List variablesTuple, + ReteNodeRecipe recipe, + RecipeTraceInfo... parentRecipeTraces) { + this(subPlan, variablesTuple, recipe, Arrays.asList(parentRecipeTraces)); + } + + public SubPlan getSubPlan() { + return subPlan; + } + + @Override + public String getPatternName() { + return subPlan.getBody().getPattern().getFullyQualifiedName(); + } + + public List getVariablesTuple() { + return variablesTuple; + } + + public Map getPosMapping() { + return posMapping; + } + + /** + * Returns a new clone that reinterprets the same compiled form + * as the compiled form of a (potentially different) subPlan. + * Useful e.g. if child plan turns out to be a no-op, or when promoting a {@link PlanningTrace} to {@link CompiledSubPlan}. + */ + public CompiledSubPlan cloneFor(SubPlan newSubPlan) { + return new CompiledSubPlan(newSubPlan, + getVariablesTuple(), + getRecipe(), + getParentRecipeTracesForCloning()); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java new file mode 100644 index 00000000..8f610550 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/RecipeTraceInfo.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.traceability; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import tools.refinery.viatra.runtime.rete.network.Node; +import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; + +/** + * A trace marker that indicates the recipe for which the node was built. + * @author Bergmann Gabor + */ +public class RecipeTraceInfo implements TraceInfo { + public ReteNodeRecipe getRecipe() {return recipe;} + /** + * For cloning in case of recursion cut-off points, use {@link #getParentRecipeTracesForCloning()} instead. + * @return an unmodifiable view on parent traces, to be constructed before this node (or alongside, in case of recursion) + */ + public List getParentRecipeTraces() {return Collections.unmodifiableList(new ArrayList<>(parentRecipeTraces));} + /** + * Directly return the underlying collection so that changes to it will be transparent. Use only for recursion-tolerant cloning. + * @noreference This method is not intended to be referenced by clients. + */ + public Collection getParentRecipeTracesForCloning() {return parentRecipeTraces;} + @Override + public Node getNode() {return node;} + + private Node node; + ReteNodeRecipe recipe; + ReteNodeRecipe shadowedRecipe; + Collection parentRecipeTraces; + + + public RecipeTraceInfo(ReteNodeRecipe recipe, Collection parentRecipeTraces) { + super(); + this.recipe = recipe; + this.parentRecipeTraces = parentRecipeTraces; //ParentTraceList.from(parentRecipeTraces); + } + public RecipeTraceInfo(ReteNodeRecipe recipe, RecipeTraceInfo... parentRecipeTraces) { + this(recipe, Arrays.asList(parentRecipeTraces)); + } + + @Override + public boolean propagateToIndexerParent() {return false;} + @Override + public boolean propagateFromIndexerToSupplierParent() {return false;} + @Override + public boolean propagateFromStandardNodeToSupplierParent() {return false;} + @Override + public boolean propagateToProductionNodeParentAlso() {return false;} + @Override + public void assignNode(Node node) {this.node = node;} + + /** + * @param knownRecipe a known recipe that is equivalent to the current recipe + */ + public void shadowWithEquivalentRecipe(ReteNodeRecipe knownRecipe) { + this.shadowedRecipe = this.recipe; + this.recipe = knownRecipe; + } + + /** + * Get original recipe shadowed by an equivalent + */ + public ReteNodeRecipe getShadowedRecipe() { + return shadowedRecipe; + } + + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.java new file mode 100644 index 00000000..e1d440db --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/TraceInfo.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.traceability; + +import tools.refinery.viatra.runtime.rete.network.Node; + + +/** + * Traces the node back to a purpose for which the node was built, + * to explain why the node is there and what it means. + * @author Bergmann Gabor + */ +public interface TraceInfo { + boolean propagateToIndexerParent(); + boolean propagateFromIndexerToSupplierParent(); + boolean propagateFromStandardNodeToSupplierParent(); + boolean propagateToProductionNodeParentAlso(); + + void assignNode(Node node); + Node getNode(); +} +// /** +// * The semantics of the tuples contained in this node. +// * @return a tuple of correct size representing the semantics of each position. +// * @post not null +// */ +// Tuple getSemantics(); \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java new file mode 100644 index 00000000..11e4db32 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/traceability/UserRequestTrace.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.traceability; + +import java.util.Collection; + +import tools.refinery.viatra.runtime.rete.recipes.ReteNodeRecipe; + +// private class AggregatorReferenceIndexTraceInfo extends RecipeTraceInfo { +// RecipeTraceInfo aggregatorNodeRecipeTrace; +// public AggregatorReferenceIndexTraceInfo(ProjectionIndexerRecipe recipe, +// RecipeTraceInfo parentRecipeTrace, +// RecipeTraceInfo aggregatorNodeRecipeTrace) { +// super(recipe, parentRecipeTrace); +// this.aggregatorNodeRecipeTrace = aggregatorNodeRecipeTrace; +// } +// public RecipeTraceInfo getAggregatorNodeRecipeTrace() { +// return aggregatorNodeRecipeTrace; +// } +// } +public class UserRequestTrace extends RecipeTraceInfo { + public UserRequestTrace(ReteNodeRecipe recipe, + Collection parentRecipeTraces) { + super(recipe, parentRecipeTraces); + } + public UserRequestTrace(ReteNodeRecipe recipe, + RecipeTraceInfo... parentRecipeTraces) { + super(recipe, parentRecipeTraces); + } +} \ No newline at end of file diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java new file mode 100644 index 00000000..0efc50af --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/LexicographicComparator.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.util; + +import java.util.Comparator; +import java.util.Iterator; + +/** + * A comparator that compares two iterables based on the lexicographic sorting induced by a comparator on elements. + * @author Bergmann Gabor + * + */ +public class LexicographicComparator implements Comparator> { + + final Comparator elementComparator; + + public LexicographicComparator(Comparator elementComparator) { + super(); + this.elementComparator = elementComparator; + } + + @Override + public int compare(Iterable o1, Iterable o2) { + Iterator it1 = o1.iterator(); + Iterator it2 = o2.iterator(); + + boolean has1, has2, bothHaveNext; + do { + has1 = it1.hasNext(); + has2 = it2.hasNext(); + bothHaveNext = has1 && has2; + if (bothHaveNext) { + T element1 = it1.next(); + T element2 = it2.next(); + int elementComparison = elementComparator.compare(element1, element2); + if (elementComparison != 0) + return elementComparison; + } + } while (bothHaveNext); + if (has1 && !has2) { + return +1; + } else if (!has1 && has2) { + return -1; + } else /*if (!has1 && !has2)*/ { + return 0; + } + } + + + + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java new file mode 100644 index 00000000..96cc445f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/Options.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.util; + +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; +import tools.refinery.viatra.runtime.matchers.planning.IQueryPlannerStrategy; +import tools.refinery.viatra.runtime.rete.construction.basiclinear.BasicLinearLayout; +import tools.refinery.viatra.runtime.rete.construction.quasitree.QuasiTreeLayout; +import tools.refinery.viatra.runtime.rete.network.communication.timely.TimelyCommunicationGroup; + +/** + * Feature switches. + * @author Gabor Bergmann + * @noreference + */ +public class Options { + + public enum NodeSharingOption { + NEVER, // not recommended, patternmatcher leaks possible + INDEXER_AND_REMOTEPROXY, ALL + } + + public static final NodeSharingOption nodeSharingOption = NodeSharingOption.ALL; + public static final boolean releaseOnetimeIndexers = true; // effective only + // with + // nodesharing + // ==NEVER + + public enum InjectivityStrategy { + EAGER, LAZY + } + + public static final InjectivityStrategy injectivityStrategy = InjectivityStrategy.EAGER; + + public static final boolean enableInheritance = true; + + // public final static boolean useComplementerMask = true; + + public static final boolean employTrivialIndexers = true; + + // public final static boolean synchronous = false; + + public static final int numberOfLocalContainers = 1; + public static final int firstFreeContainer = 0; // 0 if head container is + // free to contain pattern + // bodies, 1 otherwise + + /** + * Enable for internal debugging of Rete communication scheme; + * catches cases where the topological sort is violated by a message sent "backwards" + * @since 1.6 + */ + public static final boolean MONITOR_VIOLATION_OF_RETE_NODEGROUP_TOPOLOGICAL_SORTING = false; + + /** + * Enable for internal debugging of message delivery in {@link TimelyCommunicationGroup}s; + * catches cases when there is a violation of increasing timestamps during message delivery within a group. + * @since 2.3 + */ + public static final boolean MONITOR_VIOLATION_OF_DIFFERENTIAL_DATAFLOW_TIMESTAMPS = false; + + /** + * + * @author Gabor Bergmann + * @noreference + */ + public enum BuilderMethod { + LEGACY, // ONLY with GTASM + PSYSTEM_BASIC_LINEAR, PSYSTEM_QUASITREE; + /** + * @since 1.5 + */ + public IQueryPlannerStrategy layoutStrategy(IQueryBackendContext bContext, IQueryBackendHintProvider hintProvider) { + switch (this) { + case PSYSTEM_BASIC_LINEAR: + return new BasicLinearLayout(bContext); + case PSYSTEM_QUASITREE: + return new QuasiTreeLayout(bContext, hintProvider); + default: + throw new UnsupportedOperationException(); + } + } + } + + public static final BuilderMethod builderMethod = + // BuilderMethod.PSYSTEM_BASIC_LINEAR; + BuilderMethod.PSYSTEM_QUASITREE; + + public enum FunctionalDependencyOption { + OFF, + OPPORTUNISTIC + } + public static final FunctionalDependencyOption functionalDependencyOption = + FunctionalDependencyOption.OPPORTUNISTIC; + + public enum PlanTrimOption { + OFF, + OPPORTUNISTIC + } + public static final PlanTrimOption planTrimOption = + PlanTrimOption.OPPORTUNISTIC; + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java new file mode 100644 index 00000000..8b147cf6 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/OrderingCompareAgent.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.util; + +import java.util.Comparator; + +/** + * Comparing agent for an ordering. Terminology: the "preferred" item will register as LESS. + * + * @author Gabor Bergmann + * + */ +public abstract class OrderingCompareAgent { + protected T a; + protected T b; + + /** + * @param a + * @param b + */ + public OrderingCompareAgent(T a, T b) { + super(); + this.a = a; + this.b = b; + } + + int result = 0; + + protected abstract void doCompare(); + + /** + * @return the result + */ + public int compare() { + doCompare(); + return result; + } + + // COMPARISON HELPERS + protected boolean isUnknown() { + return result == 0; + } + + /** + * @pre result == 0 + */ + protected boolean consider(int partial) { + if (isUnknown()) + result = partial; + return isUnknown(); + } + + protected boolean swallowBoolean(boolean x) { + return x; + } + + // PREFERENCE FUNCTIONS + protected static int dontCare() { + return 0; + } + + protected static int preferTrue(boolean b1, boolean b2) { + return (b1 ^ b2) ? (b1 ? -1 : +1) : 0; + } + + protected static int preferFalse(boolean b1, boolean b2) { + return (b1 ^ b2) ? (b2 ? -1 : +1) : 0; + } + + protected static int preferLess(Comparable c1, U c2) { + return c1.compareTo(c2); + } + + protected static int preferLess(U c1, U c2, Comparator comp) { + return comp.compare(c1, c2); + } + + protected static int preferMore(Comparable c1, U c2) { + return -c1.compareTo(c2); + } + protected static int preferMore(U c1, U c2, Comparator comp) { + return -comp.compare(c1, c2); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java new file mode 100644 index 00000000..6e685253 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/util/ReteHintOptions.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.util; + +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.backend.QueryHintOption; +import tools.refinery.viatra.runtime.rete.matcher.DRedReteBackendFactory; + +/** + * Provides key objects (of type {@link QueryHintOption}) for {@link QueryEvaluationHint}s. + * @author Gabor Bergmann + * @since 1.5 + */ +public final class ReteHintOptions { + + private ReteHintOptions() {/*Utility class constructor*/} + + public static final QueryHintOption useDiscriminatorDispatchersForConstantFiltering = + hintOption("useDiscriminatorDispatchersForConstantFiltering", true); + + public static final QueryHintOption prioritizeConstantFiltering = + hintOption("prioritizeConstantFiltering", true); + + public static final QueryHintOption cacheOutputOfEvaluatorsByDefault = + hintOption("cacheOutputOfEvaluatorsByDefault", true); + + /** + * The incremental query evaluator backend can evaluate recursive patterns. + * However, by default, instance models that contain cycles are not supported with recursive queries + * and can lead to incorrect query results. + * Enabling Delete And Rederive (DRED) mode guarantees that recursive query evaluation leads to correct results in these cases as well. + * + *

As DRED may diminish the performance of incremental maintenance, it is not enabled by default. + * @since 1.6 + * @deprecated Use {@link DRedReteBackendFactory} instead of setting this option to true. + */ + @Deprecated + public static final QueryHintOption deleteRederiveEvaluation = + hintOption("deleteRederiveEvaluation", false); + + /** + * This hint allows the query planner to take advantage of "weakened alternative" suggestions of the meta context. + * For instance, enumerable unary type constraints may be substituted with a simple type filtering where sufficient. + * + * @since 1.6 + */ + public static final QueryHintOption expandWeakenedAlternativeConstraints = + hintOption("expandWeakenedAlternativeConstraints", true); + + // internal helper for conciseness + private static QueryHintOption hintOption(String hintKeyLocalName, T defaultValue) { + return new QueryHintOption<>(ReteHintOptions.class, hintKeyLocalName, defaultValue); + } +} -- cgit v1.2.3-54-g00ecf From d48a3d35bdcc6dcb15730e8daddfeec737df38b9 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sat, 19 Aug 2023 03:14:50 +0200 Subject: refactor: merge VIATRA subprojects Since we remove EMF dependencies, there is no point to maintaining a base index separate from the rest of the runtime. --- settings.gradle.kts | 2 - subprojects/viatra-runtime-base-itc/about.html | 26 - .../viatra-runtime-base-itc/build.gradle.kts | 13 - .../runtime/base/itc/alg/counting/CountingAlg.java | 226 -------- .../base/itc/alg/counting/CountingTcRelation.java | 259 --------- .../viatra/runtime/base/itc/alg/dred/DRedAlg.java | 308 ---------- .../runtime/base/itc/alg/dred/DRedTcRelation.java | 223 ------- .../runtime/base/itc/alg/fw/FloydWarshallAlg.java | 110 ---- .../base/itc/alg/incscc/CountingListener.java | 36 -- .../runtime/base/itc/alg/incscc/IncSCCAlg.java | 645 --------------------- .../runtime/base/itc/alg/misc/DFSPathFinder.java | 146 ----- .../viatra/runtime/base/itc/alg/misc/Edge.java | 38 -- .../runtime/base/itc/alg/misc/GraphHelper.java | 173 ------ .../base/itc/alg/misc/IGraphPathFinder.java | 67 --- .../runtime/base/itc/alg/misc/ITcRelation.java | 31 - .../viatra/runtime/base/itc/alg/misc/Tuple.java | 60 -- .../viatra/runtime/base/itc/alg/misc/bfs/BFS.java | 151 ----- .../runtime/base/itc/alg/misc/dfs/DFSAlg.java | 93 --- .../runtime/base/itc/alg/misc/scc/PKAlg.java | 179 ------ .../viatra/runtime/base/itc/alg/misc/scc/SCC.java | 146 ----- .../runtime/base/itc/alg/misc/scc/SCCProperty.java | 37 -- .../runtime/base/itc/alg/misc/scc/SCCResult.java | 81 --- .../itc/alg/misc/topsort/TopologicalSorting.java | 77 --- .../RepresentativeElectionAlgorithm.java | 140 ----- .../alg/representative/RepresentativeObserver.java | 12 - .../StronglyConnectedComponentAlgorithm.java | 66 --- .../WeaklyConnectedComponentAlgorithm.java | 85 --- .../base/itc/alg/util/CollectionHelper.java | 64 -- .../runtime/base/itc/graphimpl/DotGenerator.java | 160 ----- .../viatra/runtime/base/itc/graphimpl/Graph.java | 185 ------ .../itc/igraph/IBiDirectionalGraphDataSource.java | 37 -- .../base/itc/igraph/IBiDirectionalWrapper.java | 110 ---- .../runtime/base/itc/igraph/IGraphDataSource.java | 70 --- .../runtime/base/itc/igraph/IGraphObserver.java | 55 -- .../runtime/base/itc/igraph/ITcDataSource.java | 82 --- .../runtime/base/itc/igraph/ITcObserver.java | 39 -- subprojects/viatra-runtime-matchers/about.html | 26 - .../viatra-runtime-matchers/build.gradle.kts | 15 - .../matchers/ViatraQueryRuntimeException.java | 42 -- .../matchers/aggregators/AverageAccumulator.java | 24 - .../aggregators/DoubleAverageOperator.java | 82 --- .../matchers/aggregators/DoubleSumOperator.java | 62 -- .../matchers/aggregators/ExtremumOperator.java | 135 ----- .../aggregators/IntegerAverageOperator.java | 82 --- .../matchers/aggregators/IntegerSumOperator.java | 61 -- .../matchers/aggregators/LongAverageOperator.java | 82 --- .../matchers/aggregators/LongSumOperator.java | 61 -- .../viatra/runtime/matchers/aggregators/avg.java | 39 -- .../viatra/runtime/matchers/aggregators/count.java | 33 -- .../viatra/runtime/matchers/aggregators/max.java | 44 -- .../viatra/runtime/matchers/aggregators/min.java | 44 -- .../viatra/runtime/matchers/aggregators/sum.java | 39 -- .../matchers/algorithms/OrderedIterableMerge.java | 83 --- .../runtime/matchers/algorithms/UnionFind.java | 214 ------- .../matchers/algorithms/UnionFindNodeProperty.java | 32 - .../matchers/backend/CommonQueryHintOptions.java | 36 -- .../matchers/backend/ICallDelegationStrategy.java | 89 --- .../matchers/backend/IMatcherCapability.java | 26 - .../runtime/matchers/backend/IQueryBackend.java | 68 --- .../matchers/backend/IQueryBackendFactory.java | 52 -- .../backend/IQueryBackendFactoryProvider.java | 46 -- .../backend/IQueryBackendHintProvider.java | 32 - .../matchers/backend/IQueryResultProvider.java | 202 ------- .../runtime/matchers/backend/IUpdateable.java | 27 - .../matchers/backend/QueryEvaluationHint.java | 241 -------- .../runtime/matchers/backend/QueryHintOption.java | 122 ---- .../matchers/backend/ResultProviderRequestor.java | 74 --- .../matchers/context/AbstractQueryMetaContext.java | 69 --- .../context/AbstractQueryRuntimeContext.java | 21 - .../viatra/runtime/matchers/context/IInputKey.java | 45 -- .../runtime/matchers/context/IPosetComparator.java | 35 -- .../matchers/context/IQueryBackendContext.java | 49 -- .../matchers/context/IQueryCacheContext.java | 39 -- .../matchers/context/IQueryMetaContext.java | 98 ---- .../context/IQueryResultProviderAccess.java | 31 - .../matchers/context/IQueryRuntimeContext.java | 281 --------- .../context/IQueryRuntimeContextListener.java | 27 - .../runtime/matchers/context/IndexingService.java | 36 -- .../matchers/context/InputKeyImplication.java | 103 ---- .../context/common/BaseInputKeyWrapper.java | 55 -- .../context/common/JavaTransitiveInstancesKey.java | 165 ------ .../context/surrogate/SurrogateQueryRegistry.java | 153 ----- .../memories/AbstractTrivialMaskedMemory.java | 58 -- .../memories/DefaultMaskedTupleMemory.java | 127 ---- .../memories/IdentityMaskedTupleMemory.java | 77 --- .../matchers/memories/MaskedTupleMemory.java | 385 ------------ .../memories/NullaryMaskedTupleMemory.java | 85 --- .../matchers/memories/UnaryMaskedTupleMemory.java | 143 ----- .../timely/AbstractTimelyMaskedMemory.java | 228 -------- .../timely/AbstractTimelyTrivialMaskedMemory.java | 100 ---- .../timely/TimelyDefaultMaskedTupleMemory.java | 98 ---- .../timely/TimelyIdentityMaskedTupleMemory.java | 106 ---- .../timely/TimelyNullaryMaskedTupleMemory.java | 108 ---- .../timely/TimelyUnaryMaskedTupleMemory.java | 133 ----- .../matchers/planning/IOperationCompiler.java | 108 ---- .../matchers/planning/IQueryPlannerStrategy.java | 29 - .../planning/QueryProcessingException.java | 102 ---- .../viatra/runtime/matchers/planning/SubPlan.java | 240 -------- .../runtime/matchers/planning/SubPlanFactory.java | 33 -- .../matchers/planning/helpers/BuildHelper.java | 165 ------ .../helpers/FunctionalDependencyHelper.java | 143 ----- .../planning/helpers/StatisticsHelper.java | 62 -- .../matchers/planning/helpers/TypeHelper.java | 217 ------- .../matchers/planning/operations/PApply.java | 94 --- .../matchers/planning/operations/PEnumerate.java | 76 --- .../matchers/planning/operations/PJoin.java | 64 -- .../matchers/planning/operations/POperation.java | 52 -- .../matchers/planning/operations/PProject.java | 109 ---- .../matchers/planning/operations/PStart.java | 90 --- .../runtime/matchers/psystem/BasePConstraint.java | 108 ---- .../matchers/psystem/DeferredPConstraint.java | 31 - .../matchers/psystem/EnumerablePConstraint.java | 59 -- .../matchers/psystem/IExpressionEvaluator.java | 42 -- .../matchers/psystem/IMultiQueryReference.java | 26 - .../runtime/matchers/psystem/IQueryReference.java | 33 -- .../matchers/psystem/IRelationEvaluator.java | 47 -- .../runtime/matchers/psystem/ITypeConstraint.java | 65 --- .../psystem/ITypeInfoProviderConstraint.java | 28 - .../runtime/matchers/psystem/IValueProvider.java | 27 - .../matchers/psystem/InitializablePQuery.java | 56 -- .../psystem/KeyedEnumerablePConstraint.java | 39 -- .../viatra/runtime/matchers/psystem/PBody.java | 289 --------- .../runtime/matchers/psystem/PConstraint.java | 70 --- .../runtime/matchers/psystem/PTraceable.java | 16 - .../viatra/runtime/matchers/psystem/PVariable.java | 203 ------- .../runtime/matchers/psystem/TypeJudgement.java | 153 ----- .../psystem/VariableDeferredPConstraint.java | 40 -- .../AbstractMemorylessAggregationOperator.java | 31 - .../psystem/aggregations/AggregatorType.java | 49 -- .../psystem/aggregations/BoundAggregator.java | 61 -- .../psystem/aggregations/IAggregatorFactory.java | 40 -- .../aggregations/IMultisetAggregationOperator.java | 106 ---- .../matchers/psystem/analysis/QueryAnalyzer.java | 194 ------- .../matchers/psystem/annotations/PAnnotation.java | 94 --- .../psystem/annotations/ParameterReference.java | 30 - .../basicdeferred/AggregatorConstraint.java | 98 ---- .../basicdeferred/BaseTypeSafeConstraint.java | 99 ---- .../matchers/psystem/basicdeferred/Equality.java | 96 --- .../psystem/basicdeferred/ExportedParameter.java | 108 ---- .../basicdeferred/ExpressionEvaluation.java | 80 --- .../matchers/psystem/basicdeferred/Inequality.java | 151 ----- .../psystem/basicdeferred/NegativePatternCall.java | 52 -- .../basicdeferred/PatternCallBasedDeferred.java | 118 ---- .../psystem/basicdeferred/PatternMatchCounter.java | 70 --- .../psystem/basicdeferred/RelationEvaluation.java | 57 -- .../basicdeferred/TypeFilterConstraint.java | 105 ---- .../AbstractTransitiveClosure.java | 44 -- .../BinaryReflexiveTransitiveClosure.java | 57 -- .../basicenumerables/BinaryTransitiveClosure.java | 33 -- .../psystem/basicenumerables/Connectivity.java | 11 - .../psystem/basicenumerables/ConstantValue.java | 57 -- .../basicenumerables/PositivePatternCall.java | 76 --- .../RepresentativeElectionConstraint.java | 43 -- .../psystem/basicenumerables/TypeConstraint.java | 79 --- .../matchers/psystem/queries/BasePQuery.java | 231 -------- .../matchers/psystem/queries/PDisjunction.java | 104 ---- .../matchers/psystem/queries/PParameter.java | 105 ---- .../psystem/queries/PParameterDirection.java | 35 -- .../runtime/matchers/psystem/queries/PProblem.java | 68 --- .../runtime/matchers/psystem/queries/PQueries.java | 110 ---- .../runtime/matchers/psystem/queries/PQuery.java | 154 ----- .../matchers/psystem/queries/PQueryHeader.java | 101 ---- .../matchers/psystem/queries/PVisibility.java | 37 -- .../queries/QueryInitializationException.java | 35 -- .../rewriters/AbstractRewriterTraceSource.java | 53 -- .../psystem/rewriters/ConstraintRemovalReason.java | 23 - .../rewriters/DefaultFlattenCallPredicate.java | 23 - .../psystem/rewriters/FlattenerCopier.java | 129 ----- .../psystem/rewriters/IConstraintFilter.java | 48 -- .../rewriters/IDerivativeModificationReason.java | 19 - .../psystem/rewriters/IFlattenCallPredicate.java | 50 -- .../rewriters/IPTraceableTraceProvider.java | 55 -- .../psystem/rewriters/IRewriterTraceCollector.java | 33 -- .../psystem/rewriters/IVariableRenamer.java | 59 -- .../psystem/rewriters/MappingTraceCollector.java | 135 ----- .../rewriters/NeverFlattenCallPredicate.java | 26 - .../psystem/rewriters/NopTraceCollector.java | 68 --- .../matchers/psystem/rewriters/PBodyCopier.java | 306 ---------- .../psystem/rewriters/PBodyNormalizer.java | 310 ---------- .../psystem/rewriters/PDisjunctionRewriter.java | 27 - .../rewriters/PDisjunctionRewriterCacher.java | 64 -- .../psystem/rewriters/PQueryFlattener.java | 253 -------- .../psystem/rewriters/RewriterException.java | 31 - .../psystem/rewriters/SurrogateQueryRewriter.java | 63 -- .../VariableMappingExpressionEvaluatorWrapper.java | 88 --- .../runtime/matchers/tuple/AbstractTuple.java | 136 ----- .../runtime/matchers/tuple/BaseFlatTuple.java | 20 - .../matchers/tuple/BaseLeftInheritanceTuple.java | 65 --- .../viatra/runtime/matchers/tuple/FlatTuple.java | 60 -- .../viatra/runtime/matchers/tuple/FlatTuple0.java | 46 -- .../viatra/runtime/matchers/tuple/FlatTuple1.java | 44 -- .../viatra/runtime/matchers/tuple/FlatTuple2.java | 51 -- .../viatra/runtime/matchers/tuple/FlatTuple3.java | 55 -- .../viatra/runtime/matchers/tuple/FlatTuple4.java | 59 -- .../runtime/matchers/tuple/IModifiableTuple.java | 27 - .../viatra/runtime/matchers/tuple/ITuple.java | 64 -- .../matchers/tuple/LeftInheritanceTuple.java | 172 ------ .../matchers/tuple/LeftInheritanceTuple1.java | 83 --- .../matchers/tuple/LeftInheritanceTuple2.java | 73 --- .../matchers/tuple/LeftInheritanceTuple3.java | 81 --- .../matchers/tuple/LeftInheritanceTuple4.java | 88 --- .../viatra/runtime/matchers/tuple/MaskedTuple.java | 48 -- .../viatra/runtime/matchers/tuple/Tuple.java | 69 --- .../viatra/runtime/matchers/tuple/TupleMask.java | 560 ------------------ .../viatra/runtime/matchers/tuple/TupleMask0.java | 56 -- .../runtime/matchers/tuple/TupleMaskIdentity.java | 51 -- .../runtime/matchers/tuple/TupleValueProvider.java | 48 -- .../viatra/runtime/matchers/tuple/Tuples.java | 157 ----- .../matchers/tuple/VolatileMaskedTuple.java | 50 -- .../tuple/VolatileModifiableMaskedTuple.java | 47 -- .../runtime/matchers/tuple/VolatileTuple.java | 47 -- .../viatra/runtime/matchers/util/Accuracy.java | 48 -- .../viatra/runtime/matchers/util/Clearable.java | 23 - .../runtime/matchers/util/CollectionsFactory.java | 188 ------ .../viatra/runtime/matchers/util/Direction.java | 61 -- .../matchers/util/EclipseCollectionsBagMemory.java | 86 --- .../matchers/util/EclipseCollectionsDeltaBag.java | 41 -- .../matchers/util/EclipseCollectionsFactory.java | 159 ----- .../util/EclipseCollectionsLongMultiset.java | 150 ----- .../util/EclipseCollectionsLongSetMemory.java | 212 ------- .../util/EclipseCollectionsMultiLookup.java | 226 -------- .../matchers/util/EclipseCollectionsMultiset.java | 93 --- .../matchers/util/EclipseCollectionsSetMemory.java | 94 --- .../viatra/runtime/matchers/util/EmptyMemory.java | 93 --- .../viatra/runtime/matchers/util/ICache.java | 32 - .../viatra/runtime/matchers/util/IDeltaBag.java | 26 - .../viatra/runtime/matchers/util/IMemory.java | 81 --- .../viatra/runtime/matchers/util/IMemoryView.java | 205 ------- .../viatra/runtime/matchers/util/IMultiLookup.java | 216 ------- .../matchers/util/IMultiLookupAbstract.java | 485 ---------------- .../viatra/runtime/matchers/util/IMultiset.java | 30 - .../viatra/runtime/matchers/util/IProvider.java | 30 - .../viatra/runtime/matchers/util/ISetMemory.java | 37 -- .../runtime/matchers/util/MapBackedMemoryView.java | 102 ---- .../viatra/runtime/matchers/util/MarkedMemory.java | 21 - .../matchers/util/MemoryViewBackedMapView.java | 117 ---- .../runtime/matchers/util/Preconditions.java | 208 ------- .../runtime/matchers/util/PurgableCache.java | 44 -- .../viatra/runtime/matchers/util/Sets.java | 90 --- .../viatra/runtime/matchers/util/Signed.java | 60 -- .../matchers/util/SingletonInstanceProvider.java | 29 - .../runtime/matchers/util/SingletonMemoryView.java | 105 ---- .../viatra/runtime/matchers/util/TimelyMemory.java | 517 ----------------- .../matchers/util/resumable/MaskedResumable.java | 36 -- .../runtime/matchers/util/resumable/Resumable.java | 27 - .../matchers/util/resumable/UnmaskedResumable.java | 36 -- .../matchers/util/timeline/CompactTimeline.java | 111 ---- .../runtime/matchers/util/timeline/Diff.java | 55 -- .../matchers/util/timeline/SingletonTimeline.java | 73 --- .../runtime/matchers/util/timeline/Timeline.java | 146 ----- .../runtime/matchers/util/timeline/Timelines.java | 46 -- .../viatra-runtime-rete-recipes/build.gradle.kts | 2 +- ...FaithfulParallelTimelyColumnAggregatorNode.java | 25 +- ...ithfulSequentialTimelyColumnAggregatorNode.java | 7 +- .../network/mailbox/timeless/DefaultMailbox.java | 5 +- subprojects/viatra-runtime/build.gradle.kts | 4 +- .../viatra/runtime/api/ViatraQueryEngine.java | 1 - .../runtime/base/itc/alg/counting/CountingAlg.java | 226 ++++++++ .../base/itc/alg/counting/CountingTcRelation.java | 259 +++++++++ .../viatra/runtime/base/itc/alg/dred/DRedAlg.java | 308 ++++++++++ .../runtime/base/itc/alg/dred/DRedTcRelation.java | 223 +++++++ .../runtime/base/itc/alg/fw/FloydWarshallAlg.java | 110 ++++ .../base/itc/alg/incscc/CountingListener.java | 36 ++ .../runtime/base/itc/alg/incscc/IncSCCAlg.java | 645 +++++++++++++++++++++ .../runtime/base/itc/alg/misc/DFSPathFinder.java | 146 +++++ .../viatra/runtime/base/itc/alg/misc/Edge.java | 38 ++ .../runtime/base/itc/alg/misc/GraphHelper.java | 173 ++++++ .../base/itc/alg/misc/IGraphPathFinder.java | 67 +++ .../runtime/base/itc/alg/misc/ITcRelation.java | 31 + .../viatra/runtime/base/itc/alg/misc/Tuple.java | 60 ++ .../viatra/runtime/base/itc/alg/misc/bfs/BFS.java | 151 +++++ .../runtime/base/itc/alg/misc/dfs/DFSAlg.java | 93 +++ .../runtime/base/itc/alg/misc/scc/PKAlg.java | 179 ++++++ .../viatra/runtime/base/itc/alg/misc/scc/SCC.java | 146 +++++ .../runtime/base/itc/alg/misc/scc/SCCProperty.java | 37 ++ .../runtime/base/itc/alg/misc/scc/SCCResult.java | 81 +++ .../itc/alg/misc/topsort/TopologicalSorting.java | 77 +++ .../RepresentativeElectionAlgorithm.java | 140 +++++ .../alg/representative/RepresentativeObserver.java | 12 + .../StronglyConnectedComponentAlgorithm.java | 66 +++ .../WeaklyConnectedComponentAlgorithm.java | 85 +++ .../base/itc/alg/util/CollectionHelper.java | 64 ++ .../runtime/base/itc/graphimpl/DotGenerator.java | 160 +++++ .../viatra/runtime/base/itc/graphimpl/Graph.java | 185 ++++++ .../itc/igraph/IBiDirectionalGraphDataSource.java | 37 ++ .../base/itc/igraph/IBiDirectionalWrapper.java | 110 ++++ .../runtime/base/itc/igraph/IGraphDataSource.java | 70 +++ .../runtime/base/itc/igraph/IGraphObserver.java | 55 ++ .../runtime/base/itc/igraph/ITcDataSource.java | 82 +++ .../runtime/base/itc/igraph/ITcObserver.java | 39 ++ .../matchers/ViatraQueryRuntimeException.java | 42 ++ .../matchers/aggregators/AverageAccumulator.java | 24 + .../aggregators/DoubleAverageOperator.java | 82 +++ .../matchers/aggregators/DoubleSumOperator.java | 62 ++ .../matchers/aggregators/ExtremumOperator.java | 135 +++++ .../aggregators/IntegerAverageOperator.java | 82 +++ .../matchers/aggregators/IntegerSumOperator.java | 61 ++ .../matchers/aggregators/LongAverageOperator.java | 82 +++ .../matchers/aggregators/LongSumOperator.java | 61 ++ .../viatra/runtime/matchers/aggregators/avg.java | 39 ++ .../viatra/runtime/matchers/aggregators/count.java | 33 ++ .../viatra/runtime/matchers/aggregators/max.java | 44 ++ .../viatra/runtime/matchers/aggregators/min.java | 44 ++ .../viatra/runtime/matchers/aggregators/sum.java | 39 ++ .../matchers/algorithms/OrderedIterableMerge.java | 83 +++ .../runtime/matchers/algorithms/UnionFind.java | 214 +++++++ .../matchers/algorithms/UnionFindNodeProperty.java | 32 + .../matchers/backend/CommonQueryHintOptions.java | 36 ++ .../matchers/backend/ICallDelegationStrategy.java | 89 +++ .../matchers/backend/IMatcherCapability.java | 26 + .../runtime/matchers/backend/IQueryBackend.java | 68 +++ .../matchers/backend/IQueryBackendFactory.java | 52 ++ .../backend/IQueryBackendFactoryProvider.java | 46 ++ .../backend/IQueryBackendHintProvider.java | 32 + .../matchers/backend/IQueryResultProvider.java | 202 +++++++ .../runtime/matchers/backend/IUpdateable.java | 27 + .../matchers/backend/QueryEvaluationHint.java | 241 ++++++++ .../runtime/matchers/backend/QueryHintOption.java | 122 ++++ .../matchers/backend/ResultProviderRequestor.java | 74 +++ .../matchers/context/AbstractQueryMetaContext.java | 69 +++ .../context/AbstractQueryRuntimeContext.java | 21 + .../viatra/runtime/matchers/context/IInputKey.java | 45 ++ .../runtime/matchers/context/IPosetComparator.java | 35 ++ .../matchers/context/IQueryBackendContext.java | 49 ++ .../matchers/context/IQueryCacheContext.java | 39 ++ .../matchers/context/IQueryMetaContext.java | 98 ++++ .../context/IQueryResultProviderAccess.java | 31 + .../matchers/context/IQueryRuntimeContext.java | 281 +++++++++ .../context/IQueryRuntimeContextListener.java | 27 + .../runtime/matchers/context/IndexingService.java | 36 ++ .../matchers/context/InputKeyImplication.java | 103 ++++ .../context/common/BaseInputKeyWrapper.java | 55 ++ .../context/common/JavaTransitiveInstancesKey.java | 165 ++++++ .../context/surrogate/SurrogateQueryRegistry.java | 153 +++++ .../memories/AbstractTrivialMaskedMemory.java | 58 ++ .../memories/DefaultMaskedTupleMemory.java | 127 ++++ .../memories/IdentityMaskedTupleMemory.java | 77 +++ .../matchers/memories/MaskedTupleMemory.java | 385 ++++++++++++ .../memories/NullaryMaskedTupleMemory.java | 85 +++ .../matchers/memories/UnaryMaskedTupleMemory.java | 143 +++++ .../timely/AbstractTimelyMaskedMemory.java | 228 ++++++++ .../timely/AbstractTimelyTrivialMaskedMemory.java | 100 ++++ .../timely/TimelyDefaultMaskedTupleMemory.java | 98 ++++ .../timely/TimelyIdentityMaskedTupleMemory.java | 106 ++++ .../timely/TimelyNullaryMaskedTupleMemory.java | 108 ++++ .../timely/TimelyUnaryMaskedTupleMemory.java | 133 +++++ .../matchers/planning/IOperationCompiler.java | 108 ++++ .../matchers/planning/IQueryPlannerStrategy.java | 29 + .../planning/QueryProcessingException.java | 102 ++++ .../viatra/runtime/matchers/planning/SubPlan.java | 240 ++++++++ .../runtime/matchers/planning/SubPlanFactory.java | 33 ++ .../matchers/planning/helpers/BuildHelper.java | 165 ++++++ .../helpers/FunctionalDependencyHelper.java | 143 +++++ .../planning/helpers/StatisticsHelper.java | 62 ++ .../matchers/planning/helpers/TypeHelper.java | 217 +++++++ .../matchers/planning/operations/PApply.java | 94 +++ .../matchers/planning/operations/PEnumerate.java | 76 +++ .../matchers/planning/operations/PJoin.java | 64 ++ .../matchers/planning/operations/POperation.java | 52 ++ .../matchers/planning/operations/PProject.java | 109 ++++ .../matchers/planning/operations/PStart.java | 90 +++ .../runtime/matchers/psystem/BasePConstraint.java | 108 ++++ .../matchers/psystem/DeferredPConstraint.java | 31 + .../matchers/psystem/EnumerablePConstraint.java | 59 ++ .../matchers/psystem/IExpressionEvaluator.java | 42 ++ .../matchers/psystem/IMultiQueryReference.java | 26 + .../runtime/matchers/psystem/IQueryReference.java | 33 ++ .../matchers/psystem/IRelationEvaluator.java | 47 ++ .../runtime/matchers/psystem/ITypeConstraint.java | 65 +++ .../psystem/ITypeInfoProviderConstraint.java | 28 + .../runtime/matchers/psystem/IValueProvider.java | 27 + .../matchers/psystem/InitializablePQuery.java | 56 ++ .../psystem/KeyedEnumerablePConstraint.java | 39 ++ .../viatra/runtime/matchers/psystem/PBody.java | 289 +++++++++ .../runtime/matchers/psystem/PConstraint.java | 70 +++ .../runtime/matchers/psystem/PTraceable.java | 16 + .../viatra/runtime/matchers/psystem/PVariable.java | 203 +++++++ .../runtime/matchers/psystem/TypeJudgement.java | 153 +++++ .../psystem/VariableDeferredPConstraint.java | 40 ++ .../AbstractMemorylessAggregationOperator.java | 31 + .../psystem/aggregations/AggregatorType.java | 49 ++ .../psystem/aggregations/BoundAggregator.java | 61 ++ .../psystem/aggregations/IAggregatorFactory.java | 40 ++ .../aggregations/IMultisetAggregationOperator.java | 106 ++++ .../matchers/psystem/analysis/QueryAnalyzer.java | 194 +++++++ .../matchers/psystem/annotations/PAnnotation.java | 94 +++ .../psystem/annotations/ParameterReference.java | 30 + .../basicdeferred/AggregatorConstraint.java | 98 ++++ .../basicdeferred/BaseTypeSafeConstraint.java | 99 ++++ .../matchers/psystem/basicdeferred/Equality.java | 96 +++ .../psystem/basicdeferred/ExportedParameter.java | 108 ++++ .../basicdeferred/ExpressionEvaluation.java | 80 +++ .../matchers/psystem/basicdeferred/Inequality.java | 151 +++++ .../psystem/basicdeferred/NegativePatternCall.java | 52 ++ .../basicdeferred/PatternCallBasedDeferred.java | 118 ++++ .../psystem/basicdeferred/PatternMatchCounter.java | 70 +++ .../psystem/basicdeferred/RelationEvaluation.java | 57 ++ .../basicdeferred/TypeFilterConstraint.java | 105 ++++ .../AbstractTransitiveClosure.java | 44 ++ .../BinaryReflexiveTransitiveClosure.java | 57 ++ .../basicenumerables/BinaryTransitiveClosure.java | 33 ++ .../psystem/basicenumerables/Connectivity.java | 11 + .../psystem/basicenumerables/ConstantValue.java | 57 ++ .../basicenumerables/PositivePatternCall.java | 76 +++ .../RepresentativeElectionConstraint.java | 43 ++ .../psystem/basicenumerables/TypeConstraint.java | 79 +++ .../matchers/psystem/queries/BasePQuery.java | 231 ++++++++ .../matchers/psystem/queries/PDisjunction.java | 104 ++++ .../matchers/psystem/queries/PParameter.java | 105 ++++ .../psystem/queries/PParameterDirection.java | 35 ++ .../runtime/matchers/psystem/queries/PProblem.java | 68 +++ .../runtime/matchers/psystem/queries/PQueries.java | 110 ++++ .../runtime/matchers/psystem/queries/PQuery.java | 154 +++++ .../matchers/psystem/queries/PQueryHeader.java | 101 ++++ .../matchers/psystem/queries/PVisibility.java | 37 ++ .../queries/QueryInitializationException.java | 35 ++ .../rewriters/AbstractRewriterTraceSource.java | 53 ++ .../psystem/rewriters/ConstraintRemovalReason.java | 23 + .../rewriters/DefaultFlattenCallPredicate.java | 23 + .../psystem/rewriters/FlattenerCopier.java | 129 +++++ .../psystem/rewriters/IConstraintFilter.java | 48 ++ .../rewriters/IDerivativeModificationReason.java | 19 + .../psystem/rewriters/IFlattenCallPredicate.java | 50 ++ .../rewriters/IPTraceableTraceProvider.java | 55 ++ .../psystem/rewriters/IRewriterTraceCollector.java | 33 ++ .../psystem/rewriters/IVariableRenamer.java | 59 ++ .../psystem/rewriters/MappingTraceCollector.java | 135 +++++ .../rewriters/NeverFlattenCallPredicate.java | 26 + .../psystem/rewriters/NopTraceCollector.java | 68 +++ .../matchers/psystem/rewriters/PBodyCopier.java | 306 ++++++++++ .../psystem/rewriters/PBodyNormalizer.java | 310 ++++++++++ .../psystem/rewriters/PDisjunctionRewriter.java | 27 + .../rewriters/PDisjunctionRewriterCacher.java | 64 ++ .../psystem/rewriters/PQueryFlattener.java | 253 ++++++++ .../psystem/rewriters/RewriterException.java | 31 + .../psystem/rewriters/SurrogateQueryRewriter.java | 63 ++ .../VariableMappingExpressionEvaluatorWrapper.java | 88 +++ .../runtime/matchers/tuple/AbstractTuple.java | 136 +++++ .../runtime/matchers/tuple/BaseFlatTuple.java | 20 + .../matchers/tuple/BaseLeftInheritanceTuple.java | 65 +++ .../viatra/runtime/matchers/tuple/FlatTuple.java | 60 ++ .../viatra/runtime/matchers/tuple/FlatTuple0.java | 46 ++ .../viatra/runtime/matchers/tuple/FlatTuple1.java | 44 ++ .../viatra/runtime/matchers/tuple/FlatTuple2.java | 51 ++ .../viatra/runtime/matchers/tuple/FlatTuple3.java | 55 ++ .../viatra/runtime/matchers/tuple/FlatTuple4.java | 59 ++ .../runtime/matchers/tuple/IModifiableTuple.java | 27 + .../viatra/runtime/matchers/tuple/ITuple.java | 64 ++ .../matchers/tuple/LeftInheritanceTuple.java | 172 ++++++ .../matchers/tuple/LeftInheritanceTuple1.java | 83 +++ .../matchers/tuple/LeftInheritanceTuple2.java | 73 +++ .../matchers/tuple/LeftInheritanceTuple3.java | 81 +++ .../matchers/tuple/LeftInheritanceTuple4.java | 88 +++ .../viatra/runtime/matchers/tuple/MaskedTuple.java | 48 ++ .../viatra/runtime/matchers/tuple/Tuple.java | 69 +++ .../viatra/runtime/matchers/tuple/TupleMask.java | 560 ++++++++++++++++++ .../viatra/runtime/matchers/tuple/TupleMask0.java | 56 ++ .../runtime/matchers/tuple/TupleMaskIdentity.java | 51 ++ .../runtime/matchers/tuple/TupleValueProvider.java | 48 ++ .../viatra/runtime/matchers/tuple/Tuples.java | 157 +++++ .../matchers/tuple/VolatileMaskedTuple.java | 50 ++ .../tuple/VolatileModifiableMaskedTuple.java | 47 ++ .../runtime/matchers/tuple/VolatileTuple.java | 47 ++ .../viatra/runtime/matchers/util/Accuracy.java | 48 ++ .../viatra/runtime/matchers/util/Clearable.java | 23 + .../runtime/matchers/util/CollectionsFactory.java | 188 ++++++ .../viatra/runtime/matchers/util/Direction.java | 61 ++ .../matchers/util/EclipseCollectionsBagMemory.java | 86 +++ .../matchers/util/EclipseCollectionsDeltaBag.java | 41 ++ .../matchers/util/EclipseCollectionsFactory.java | 159 +++++ .../util/EclipseCollectionsLongMultiset.java | 150 +++++ .../util/EclipseCollectionsLongSetMemory.java | 212 +++++++ .../util/EclipseCollectionsMultiLookup.java | 226 ++++++++ .../matchers/util/EclipseCollectionsMultiset.java | 93 +++ .../matchers/util/EclipseCollectionsSetMemory.java | 94 +++ .../viatra/runtime/matchers/util/EmptyMemory.java | 93 +++ .../viatra/runtime/matchers/util/ICache.java | 32 + .../viatra/runtime/matchers/util/IDeltaBag.java | 26 + .../viatra/runtime/matchers/util/IMemory.java | 81 +++ .../viatra/runtime/matchers/util/IMemoryView.java | 205 +++++++ .../viatra/runtime/matchers/util/IMultiLookup.java | 216 +++++++ .../matchers/util/IMultiLookupAbstract.java | 485 ++++++++++++++++ .../viatra/runtime/matchers/util/IMultiset.java | 30 + .../viatra/runtime/matchers/util/IProvider.java | 30 + .../viatra/runtime/matchers/util/ISetMemory.java | 37 ++ .../runtime/matchers/util/MapBackedMemoryView.java | 102 ++++ .../viatra/runtime/matchers/util/MarkedMemory.java | 21 + .../matchers/util/MemoryViewBackedMapView.java | 117 ++++ .../runtime/matchers/util/Preconditions.java | 208 +++++++ .../runtime/matchers/util/PurgableCache.java | 44 ++ .../viatra/runtime/matchers/util/Sets.java | 90 +++ .../viatra/runtime/matchers/util/Signed.java | 60 ++ .../matchers/util/SingletonInstanceProvider.java | 29 + .../runtime/matchers/util/SingletonMemoryView.java | 105 ++++ .../viatra/runtime/matchers/util/TimelyMemory.java | 517 +++++++++++++++++ .../matchers/util/resumable/MaskedResumable.java | 36 ++ .../runtime/matchers/util/resumable/Resumable.java | 27 + .../matchers/util/resumable/UnmaskedResumable.java | 36 ++ .../matchers/util/timeline/CompactTimeline.java | 111 ++++ .../runtime/matchers/util/timeline/Diff.java | 55 ++ .../matchers/util/timeline/SingletonTimeline.java | 73 +++ .../runtime/matchers/util/timeline/Timeline.java | 146 +++++ .../runtime/matchers/util/timeline/Timelines.java | 46 ++ 503 files changed, 23732 insertions(+), 23822 deletions(-) delete mode 100644 subprojects/viatra-runtime-base-itc/about.html delete mode 100644 subprojects/viatra-runtime-base-itc/build.gradle.kts delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java delete mode 100644 subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java delete mode 100644 subprojects/viatra-runtime-matchers/about.html delete mode 100644 subprojects/viatra-runtime-matchers/build.gradle.kts delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java delete mode 100644 subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/RepresentativeElectionConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple1.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple2.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple3.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple4.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/MaskedTuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java (limited to 'subprojects/viatra-runtime-rete') diff --git a/settings.gradle.kts b/settings.gradle.kts index e9eec388..15aa7a58 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -18,9 +18,7 @@ include( "store-query-viatra", "store-reasoning", "viatra-runtime", - "viatra-runtime-base-itc", "viatra-runtime-localsearch", - "viatra-runtime-matchers", "viatra-runtime-rete", "viatra-runtime-rete-recipes", ) diff --git a/subprojects/viatra-runtime-base-itc/about.html b/subprojects/viatra-runtime-base-itc/about.html deleted file mode 100644 index d1d5593a..00000000 --- a/subprojects/viatra-runtime-base-itc/about.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - -About - - - -

About This Content

- -

March 18, 2019

-

License

- -

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the -Eclipse Public License Version 2.0 ("EPL"). A copy of the EPL is available at http://www.eclipse.org/legal/epl-v20.html. -For purposes of the EPL, "Program" will mean the Content.

- -

If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may -apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise -indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at http://www.eclipse.org.

- - diff --git a/subprojects/viatra-runtime-base-itc/build.gradle.kts b/subprojects/viatra-runtime-base-itc/build.gradle.kts deleted file mode 100644 index 2fcdf5c4..00000000 --- a/subprojects/viatra-runtime-base-itc/build.gradle.kts +++ /dev/null @@ -1,13 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ - -plugins { - id("tools.refinery.gradle.java-library") -} - -dependencies { - implementation(project(":refinery-viatra-runtime-matchers")) -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java deleted file mode 100644 index d0367cde..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java +++ /dev/null @@ -1,226 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.counting; - -import java.util.List; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -/** - * This class is the optimized implementation of the Counting algorithm. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the nodes in the graph data source - */ -public class CountingAlg implements IGraphObserver, ITcDataSource { - - private CountingTcRelation tc = null; - private IBiDirectionalGraphDataSource gds = null; - private List> observers; - - /** - * Constructs a new Counting algorithm and initializes the transitive closure relation with the given graph data - * source. Attach itself on the graph data source as an observer. - * - * @param gds - * the graph data source instance - */ - public CountingAlg(IGraphDataSource gds) { - - if (gds instanceof IBiDirectionalGraphDataSource) { - this.gds = (IBiDirectionalGraphDataSource) gds; - } else { - this.gds = new IBiDirectionalWrapper(gds); - } - - observers = CollectionsFactory.>createObserverList(); - tc = new CountingTcRelation(true); - - initTc(); - gds.attachObserver(this); - } - - /** - * Initializes the transitive closure relation. - */ - private void initTc() { - this.setTcRelation(CountingTcRelation.createFrom(gds)); - } - - @Override - public void edgeInserted(V source, V target) { - if (!source.equals(target)) { - deriveTc(source, target, true); - } - } - - @Override - public void edgeDeleted(V source, V target) { - if (!source.equals(target)) { - deriveTc(source, target, false); - } - } - - @Override - public void nodeInserted(V n) { - - } - - @Override - public void nodeDeleted(V n) { - this.tc.deleteTupleEnd(n); - } - - /** - * Derives the transitive closure relation when an edge is inserted or deleted. - * - * @param source - * the source of the edge - * @param target - * the target of the edge - * @param dCount - * the value is -1 if an edge was deleted and +1 if an edge was inserted - */ - private void deriveTc(V source, V target, boolean isInsertion) { - - // if (dCount == 1 && isReachable(target, source)) { - // System.out.println("The graph contains cycle with (" + source + ","+ target + ") edge!"); - // } - - CountingTcRelation dtc = new CountingTcRelation(false); - Set tupEnds = null; - - // 1. d(tc(x,y)) :- d(l(x,y)) - if (tc.updateTuple(source, target, isInsertion)) { - dtc.updateTuple(source, target, true /* deltas implicitly have the same sign as isInsertion*/); - notifyTcObservers(source, target, isInsertion); - } - - // 2. d(tc(x,y)) :- d(l(x,z)) & tc(z,y) - tupEnds = tc.getTupleEnds(target); - if (tupEnds != null) { - for (V tupEnd : tupEnds) { - if (!tupEnd.equals(source)) { - if (tc.updateTuple(source, tupEnd, isInsertion)) { - dtc.updateTuple(source, tupEnd, true /* deltas implicitly have the same sign as isInsertion*/); - notifyTcObservers(source, tupEnd, isInsertion); - } - } - } - } - - // 3. d(tc(x,y)) :- lv(x,z) & d(tc(z,y)) - CountingTcRelation newTuples = dtc; - CountingTcRelation tmp = null; - dtc = new CountingTcRelation(false); - - IMemoryView nodes = null; - - while (!newTuples.isEmpty()) { - - tmp = dtc; - dtc = newTuples; - newTuples = tmp; - newTuples.clear(); - - for (V tS : dtc.getTupleStarts()) { - nodes = gds.getSourceNodes(tS); - for (V nS : nodes.distinctValues()) { - int count = nodes.getCount(nS); - for (int i = 0; i < count; i++) { - tupEnds = dtc.getTupleEnds(tS); - if (tupEnds != null) { - for (V tT : tupEnds) { - if (!nS.equals(tT)) { - if (tc.updateTuple(nS, tT, isInsertion)) { - newTuples.updateTuple(nS, tT, true /* deltas implicitly have the same sign as isInsertion*/); - notifyTcObservers(nS, tT, isInsertion); - } - } - } - } - } - } - } - } - - // System.out.println(tc); - } - - public ITcRelation getTcRelation() { - return this.tc; - } - - public void setTcRelation(CountingTcRelation tc) { - this.tc = tc; - } - - @Override - public boolean isReachable(V source, V target) { - return tc.containsTuple(source, target); - } - - @Override - public void attachObserver(ITcObserver to) { - this.observers.add(to); - - } - - @Override - public void detachObserver(ITcObserver to) { - this.observers.remove(to); - } - - @Override - public Set getAllReachableTargets(V source) { - return tc.getTupleEnds(source); - } - - @Override - public Set getAllReachableSources(V target) { - return tc.getTupleStarts(target); - } - - private void notifyTcObservers(V source, V target, boolean isInsertion) { - if (isInsertion) { - for (ITcObserver o : observers) { - o.tupleInserted(source, target); - } - } else { - for (ITcObserver o : observers) { - o.tupleDeleted(source, target); - } - } - } - - @Override - public void dispose() { - tc.clear(); - this.gds.detachObserver(this); - } - - @Override - public IGraphPathFinder getPathFinder() { - return new DFSPathFinder(gds, this); - } -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java deleted file mode 100644 index 8f804dfd..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java +++ /dev/null @@ -1,259 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.counting; - -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; -import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; - -/** - * Transitive closure relation implementation for the Counting algorithm. - * - * @author Tamas Szabo - * - * @param - */ -public class CountingTcRelation implements ITcRelation { - - private IMultiLookup tuplesForward = null; - private IMultiLookup tuplesBackward = null; - - protected CountingTcRelation(boolean backwardIndexing) { - tuplesForward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); - if (backwardIndexing) - tuplesBackward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); - } - - protected boolean isEmpty() { - return 0 == this.tuplesForward.countKeys(); - } - - protected void clear() { - this.tuplesForward.clear(); - - if (tuplesBackward != null) { - this.tuplesBackward.clear(); - } - } - - protected void union(CountingTcRelation rA) { - IMultiLookup rForward = rA.tuplesForward; - for (V source : rForward.distinctKeys()) { - IMemoryView targetBag = rForward.lookup(source); - for (V target : targetBag.distinctValues()) { - this.addTuple(source, target, targetBag.getCount(target)); - } - } - } - - public int getCount(V source, V target) { - IMemoryView bucket = tuplesForward.lookup(source); - return bucket == null ? 0 : bucket.getCount(target); - } - - /** - * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false - * otherwise (in this case count is incremented with the given count parameter). - * - * @param source - * the source of the tuple - * @param target - * the target of the tuple - * @param count - * the count of the tuple, must be positive - * @return true if the relation did not contain previously the tuple - */ - public boolean addTuple(V source, V target, int count) { - if (tuplesBackward != null) { - tuplesBackward.addPairPositiveMultiplicity(target, source, count); - } - - ChangeGranularity change = - tuplesForward.addPairPositiveMultiplicity(source, target, count); - - return change != ChangeGranularity.DUPLICATE; - } - - /** - * Derivation count of the tuple (source,target) is incremented or decremented. - * Returns true iff updated to / from zero derivation count. - * @since 1.7 - */ - public boolean updateTuple(V source, V target, boolean isInsertion) { - if (isInsertion) { - if (tuplesBackward != null) { - tuplesBackward.addPair(target, source); - } - ChangeGranularity change = - tuplesForward.addPair(source, target); - return change != ChangeGranularity.DUPLICATE; - } else { - if (tuplesBackward != null) { - tuplesBackward.removePair(target, source); - } - ChangeGranularity change = - tuplesForward.removePair(source, target); - return change != ChangeGranularity.DUPLICATE; - } - } - - public void deleteTupleEnd(V deleted) { - Set sourcesToDelete = CollectionsFactory.createSet(); - Set targetsToDelete = CollectionsFactory.createSet(); - - for (V target : tuplesForward.lookupOrEmpty(deleted).distinctValues()) { - targetsToDelete.add(target); - } - if (tuplesBackward != null) { - for (V source : tuplesBackward.lookupOrEmpty(deleted).distinctValues()) { - sourcesToDelete.add(source); - } - } else { - for (V sourceCandidate : tuplesForward.distinctKeys()) { - if (tuplesForward.lookupOrEmpty(sourceCandidate).containsNonZero(deleted)) - sourcesToDelete.add(sourceCandidate); - } - } - - for (V source : sourcesToDelete) { - int count = tuplesForward.lookupOrEmpty(source).getCount(deleted); - for (int i=0; i< count; ++i) tuplesForward.removePair(source, deleted); - } - for (V target : targetsToDelete) { - int count = tuplesForward.lookupOrEmpty(deleted).getCount(target); - for (int i=0; i< count; ++i) tuplesForward.removePair(deleted, target); - } - - if (tuplesBackward != null) { - for (V source : sourcesToDelete) { - int count = tuplesBackward.lookupOrEmpty(deleted).getCount(source); - for (int i=0; i< count; ++i) tuplesBackward.removePair(deleted, source); - } - for (V target : targetsToDelete) { - int count = tuplesBackward.lookupOrEmpty(target).getCount(deleted); - for (int i=0; i< count; ++i) tuplesBackward.removePair(target, deleted); - } - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("TcRelation = "); - - for (V source : tuplesForward.distinctKeys()) { - IMemoryView targets = tuplesForward.lookup(source); - for (V target : targets.distinctValues()) { - sb.append("{(" + source + "," + target + ")," + targets.getCount(target) + "} "); - } - } - - return sb.toString(); - } - - @Override - public Set getTupleEnds(V source) { - IMemoryView tupEnds = tuplesForward.lookup(source); - if (tupEnds == null) - return null; - return tupEnds.distinctValues(); - } - - /** - * Returns the set of nodes from which the target node is reachable, if already computed. - * - * @param target - * the target node - * @return the set of source nodes - * @throws UnsupportedOperationException if backwards index not computed - */ - public Set getTupleStarts(V target) { - if (tuplesBackward != null) { - IMemoryView tupStarts = tuplesBackward.lookup(target); - if (tupStarts == null) - return null; - return tupStarts.distinctValues(); - } else { - throw new UnsupportedOperationException("built without backward indexing"); - } - } - - @Override - public Set getTupleStarts() { - Set nodes = CollectionsFactory.createSet(); - for (V s : tuplesForward.distinctKeys()) { - nodes.add(s); - } - return nodes; - } - - /** - * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise. - * - * @param source - * the source node - * @param target - * the target node - * @return true if tuple is present, false otherwise - */ - public boolean containsTuple(V source, V target) { - return tuplesForward.lookupOrEmpty(source).containsNonZero(target); - } - - @SuppressWarnings("unchecked") - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } else if (obj == null || this.getClass() != obj.getClass()) { - return false; - } else { - CountingTcRelation aTR = (CountingTcRelation) obj; - - return tuplesForward.equals(aTR.tuplesForward); - } - } - - @Override - public int hashCode() { - return tuplesForward.hashCode(); - } - - public static CountingTcRelation createFrom(IBiDirectionalGraphDataSource gds) { - List topologicalSorting = TopologicalSorting.compute(gds); - CountingTcRelation tc = new CountingTcRelation(true); - Collections.reverse(topologicalSorting); - for (V n : topologicalSorting) { - IMemoryView sourceNodes = gds.getSourceNodes(n); - Set tupEnds = tc.getTupleEnds(n); - for (V s : sourceNodes.distinctValues()) { - int count = sourceNodes.getCount(s); - for (int i = 0; i < count; i++) { - tc.updateTuple(s, n, true); - if (tupEnds != null) { - for (V t : tupEnds) { - tc.updateTuple(s, t, true); - } - } - } - } - } - - return tc; - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java deleted file mode 100644 index b92d08bf..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java +++ /dev/null @@ -1,308 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.dred; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple; -import tools.refinery.viatra.runtime.base.itc.alg.misc.dfs.DFSAlg; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -/** - * This class is the optimized implementation of the DRED algorithm. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the nodes in the graph data source - */ -public class DRedAlg implements IGraphObserver, ITcDataSource { - - private IGraphDataSource graphDataSource = null; - private DRedTcRelation tc = null; - private DRedTcRelation dtc = null; - private List> observers; - - /** - * Constructs a new DRED algorithm and initializes the transitive closure relation with the given graph data source. - * Attach itself on the graph data source as an observer. - * - * @param gds - * the graph data source instance - */ - public DRedAlg(IGraphDataSource gds) { - this.observers = new ArrayList>(); - this.graphDataSource = gds; - this.tc = new DRedTcRelation(); - this.dtc = new DRedTcRelation(); - initTc(); - graphDataSource.attachObserver(this); - } - - /** - * Constructs a new DRED algorithm and initializes the transitive closure relation with the given relation. Attach - * itself on the graph data source as an observer. - * - * @param gds - * the graph data source instance - * @param tc - * the transitive closure instance - */ - public DRedAlg(IGraphDataSource gds, DRedTcRelation tc) { - this.graphDataSource = gds; - this.tc = tc; - this.dtc = new DRedTcRelation(); - graphDataSource.attachObserver(this); - } - - /** - * Initializes the transitive closure relation. - */ - private void initTc() { - DFSAlg dfsa = new DFSAlg(this.graphDataSource); - this.setTcRelation(dfsa.getTcRelation()); - this.graphDataSource.detachObserver(dfsa); - } - - @Override - public void edgeInserted(V source, V target) { - if (!source.equals(target)) { - Set tupStarts = null; - Set tupEnds = null; - Set> tuples = new HashSet>(); - - if (!source.equals(target) && tc.addTuple(source, target)) { - tuples.add(new Tuple(source, target)); - } - - // 2. d+(tc(x,y)) :- d+(tc(x,z)) & lv(z,y) Descartes product - tupStarts = tc.getTupleStarts(source); - tupEnds = tc.getTupleEnds(target); - - for (V s : tupStarts) { - for (V t : tupEnds) { - if (!s.equals(t) && tc.addTuple(s, t)) { - tuples.add(new Tuple(s, t)); - } - } - } - - // (s, source) -> (source, target) - // tupStarts = tc.getTupleStarts(source); - for (V s : tupStarts) { - if (!s.equals(target) && tc.addTuple(s, target)) { - tuples.add(new Tuple(s, target)); - } - } - - // (source, target) -> (target, t) - // tupEnds = tc.getTupleEnds(target); - for (V t : tupEnds) { - if (!source.equals(t) && tc.addTuple(source, t)) { - tuples.add(new Tuple(source, t)); - } - } - - notifyTcObservers(tuples, 1); - } - } - - @Override - public void edgeDeleted(V source, V target) { - if (!source.equals(target)) { - - // Computing overestimate, Descartes product of A and B sets, where - // A: those nodes from which source is reachable - // B: those nodes which is reachable from target - - Map, Integer> tuples = new HashMap, Integer>(); - Set sources = tc.getTupleStarts(source); - Set targets = tc.getTupleEnds(target); - - tc.removeTuple(source, target); - tuples.put(new Tuple(source, target), -1); - - for (V s : sources) { - for (V t : targets) { - if (!s.equals(t)) { - tc.removeTuple(s, t); - tuples.put(new Tuple(s, t), -1); - } - } - } - - for (V s : sources) { - if (!s.equals(target)) { - tc.removeTuple(s, target); - tuples.put(new Tuple(s, target), -1); - } - } - - for (V t : targets) { - if (!source.equals(t)) { - tc.removeTuple(source, t); - tuples.put(new Tuple(source, t), -1); - } - } - - // System.out.println("overestimate: "+dtc); - - // Modify overestimate with those tuples that have alternative derivations - // 1. q+(tc(x,y)) :- lv(x,y) - for (V s : graphDataSource.getAllNodes()) { - IMemoryView targetNodes = graphDataSource.getTargetNodes(s); - for (Entry entry : targetNodes.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V t = entry.getKey(); - if (!s.equals(t)) { - tc.addTuple(s, t); - Tuple tuple = new Tuple(s, t); - Integer count = tuples.get(tuple); - if (count != null && count == -1) { - tuples.remove(tuple); - } - } - - } - } - } - - // 2. q+(tc(x,y)) :- tcv(x,z) & lv(z,y) - DRedTcRelation newTups = new DRedTcRelation(); - dtc.clear(); - dtc.union(tc); - - while (!dtc.isEmpty()) { - - newTups.clear(); - newTups.union(dtc); - dtc.clear(); - - for (V s : newTups.getTupleStarts()) { - for (V t : newTups.getTupleEnds(s)) { - IMemoryView targetNodes = graphDataSource.getTargetNodes(t); - if (targetNodes != null) { - for (Entry entry : targetNodes.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V tn = entry.getKey(); - if (!s.equals(tn) && tc.addTuple(s, tn)) { - dtc.addTuple(s, tn); - tuples.remove(new Tuple(s, tn)); - } - } - } - } - } - } - } - - notifyTcObservers(tuples.keySet(), -1); - } - } - - @Override - public void nodeInserted(V n) { - // Node inserted does not result new tc tuple. - } - - @Override - public void nodeDeleted(V n) { - // FIXME node deletion may involve the deletion of incoming and outgoing edges too - Set set = tc.getTupleEnds(n); - Set modSet = null; - - // n -> target - modSet = new HashSet(set); - - for (V tn : modSet) { - this.tc.removeTuple(n, tn); - } - - // source -> n - set = tc.getTupleStarts(n); - - modSet = new HashSet(set); - - for (V sn : modSet) { - this.tc.removeTuple(sn, n); - } - } - - public DRedTcRelation getTcRelation() { - return this.tc; - } - - public void setTcRelation(DRedTcRelation tc) { - this.tc = tc; - } - - @Override - public boolean isReachable(V source, V target) { - return tc.containsTuple(source, target); - } - - @Override - public void attachObserver(ITcObserver to) { - this.observers.add(to); - } - - @Override - public void detachObserver(ITcObserver to) { - this.observers.remove(to); - } - - @Override - public Set getAllReachableTargets(V source) { - return tc.getTupleEnds(source); - } - - @Override - public Set getAllReachableSources(V target) { - return tc.getTupleStarts(target); - } - - protected void notifyTcObservers(Set> tuples, int dir) { - for (ITcObserver o : observers) { - for (Tuple t : tuples) { - if (!t.getSource().equals(t.getTarget())) { - if (dir == 1) { - o.tupleInserted(t.getSource(), t.getTarget()); - } - if (dir == -1) { - o.tupleDeleted(t.getSource(), t.getTarget()); - } - } - } - } - } - - @Override - public void dispose() { - tc = null; - dtc = null; - } - - @Override - public IGraphPathFinder getPathFinder() { - return new DFSPathFinder(graphDataSource, this); - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java deleted file mode 100644 index 8543b79c..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java +++ /dev/null @@ -1,223 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.dred; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; - -public class DRedTcRelation implements ITcRelation { - - // tc(a,b) means that b is transitively reachable from a - private Map> tuplesForward; - - // data structure to efficiently get those nodes from which a given node is reachable - // symmetric to tuplesForward - private Map> tuplesBackward; - - public DRedTcRelation() { - this.tuplesForward = new HashMap>(); - this.tuplesBackward = new HashMap>(); - } - - public void clear() { - this.tuplesForward.clear(); - this.tuplesBackward.clear(); - } - - public boolean isEmpty() { - return tuplesForward.isEmpty(); - } - - public void removeTuple(V source, V target) { - - // removing tuple from 'forward' tc relation - Set sSet = tuplesForward.get(source); - if (sSet != null) { - sSet.remove(target); - if (sSet.size() == 0) - tuplesForward.remove(source); - } - - // removing tuple from 'backward' tc relation - Set tSet = tuplesBackward.get(target); - if (tSet != null) { - tSet.remove(source); - if (tSet.size() == 0) - tuplesBackward.remove(target); - } - } - - /** - * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false - * otherwise. - * - * @param source - * the source of the tuple - * @param target - * the target of the tuple - * @return true if the relation did not contain previously the tuple - */ - public boolean addTuple(V source, V target) { - - // symmetric modification, it is sufficient to check the return value in one collection - // adding tuple to 'forward' tc relation - Set sSet = tuplesForward.get(source); - if (sSet == null) { - Set newSet = new HashSet(); - newSet.add(target); - tuplesForward.put(source, newSet); - } else { - sSet.add(target); - } - - // adding tuple to 'backward' tc relation - Set tSet = tuplesBackward.get(target); - if (tSet == null) { - Set newSet = new HashSet(); - newSet.add(source); - tuplesBackward.put(target, newSet); - return true; - } else { - boolean ret = tSet.add(source); - return ret; - } - - } - - /** - * Union operation of two tc realtions. - * - * @param rA - * the other tc relation - */ - public void union(DRedTcRelation rA) { - for (V source : rA.tuplesForward.keySet()) { - for (V target : rA.tuplesForward.get(source)) { - this.addTuple(source, target); - } - } - } - - /** - * Computes the difference of this tc relation and the given rA parameter. - * - * @param rA - * the subtrahend relation - */ - public void difference(DRedTcRelation rA) { - for (V source : rA.tuplesForward.keySet()) { - for (V target : rA.tuplesForward.get(source)) { - this.removeTuple(source, target); - } - } - } - - @Override - public Set getTupleEnds(V source) { - Set t = tuplesForward.get(source); - return (t == null) ? new HashSet() : new HashSet(t); - } - - /** - * Returns the set of nodes from which the target node is reachable. - * - * @param target - * the target node - * @return the set of source nodes - */ - public Set getTupleStarts(V target) { - Set t = tuplesBackward.get(target); - return (t == null) ? new HashSet() : new HashSet(t); - } - - @Override - public Set getTupleStarts() { - Set t = tuplesForward.keySet(); - return new HashSet(t); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("TcRelation = "); - - for (Entry> entry : this.tuplesForward.entrySet()) { - V source = entry.getKey(); - for (V target : entry.getValue()) { - sb.append("(" + source + "," + target + ") "); - } - } - return sb.toString(); - } - - /** - * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise. - * - * @param source - * the source node - * @param target - * the target node - * @return true if tuple is present, false otherwise - */ - public boolean containsTuple(V source, V target) { - if (tuplesForward.containsKey(source)) { - if (tuplesForward.get(source).contains(target)) - return true; - } - return false; - } - - @SuppressWarnings("unchecked") - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if ((obj == null) || (obj.getClass() != this.getClass())) { - return false; - } - - DRedTcRelation aTR = (DRedTcRelation) obj; - - for (Entry> entry : aTR.tuplesForward.entrySet()) { - V source = entry.getKey(); - for (V target : entry.getValue()) { - if (!this.containsTuple(source, target)) - return false; - } - } - - for (Entry> entry : this.tuplesForward.entrySet()) { - V source = entry.getKey(); - for (V target : entry.getValue()) { - if (!aTR.containsTuple(source, target)) - return false; - } - } - - return true; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 31 * hash + tuplesForward.hashCode(); - hash = 31 * hash + tuplesBackward.hashCode(); - return hash; - } - - public Map> getTuplesForward() { - return tuplesForward; - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java deleted file mode 100644 index b369f1a1..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.fw; - -import java.util.HashMap; -import java.util.Map; - -import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -public class FloydWarshallAlg implements IGraphObserver { - - private DRedTcRelation tc = null; - private IBiDirectionalGraphDataSource gds = null; - - public FloydWarshallAlg(IGraphDataSource gds) { - if (gds instanceof IBiDirectionalGraphDataSource) { - this.gds = (IBiDirectionalGraphDataSource) gds; - } else { - this.gds = new IBiDirectionalWrapper(gds); - } - - this.tc = new DRedTcRelation(); - gds.attachObserver(this); - generateTc(); - } - - private void generateTc() { - - tc.clear(); - - int n = gds.getAllNodes().size(); - Map mapForw = new HashMap(); - Map mapBackw = new HashMap(); - int[][] P = new int[n][n]; - - int i, j, k; - - // initialize adjacent matrix - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - P[i][j] = 0; - } - } - - i = 0; - for (V node : gds.getAllNodes()) { - mapForw.put(node, i); - mapBackw.put(i, node); - i++; - } - - for (V source : gds.getAllNodes()) { - IMemoryView targets = gds.getTargetNodes(source); - for (V target : targets.distinctValues()) { - P[mapForw.get(source)][mapForw.get(target)] = 1; - } - } - - for (k = 0; k < n; k++) { - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - P[i][j] = P[i][j] | (P[i][k] & P[k][j]); - } - } - } - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - if (P[i][j] == 1 && i != j) - tc.addTuple(mapBackw.get(i), mapBackw.get(j)); - } - } - } - - @Override - public void edgeInserted(V source, V target) { - generateTc(); - } - - @Override - public void edgeDeleted(V source, V target) { - generateTc(); - } - - @Override - public void nodeInserted(V n) { - generateTc(); - } - - @Override - public void nodeDeleted(V n) { - generateTc(); - } - - public DRedTcRelation getTcRelation() { - return this.tc; - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java deleted file mode 100644 index fdf64f77..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.alg.incscc; - -import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; -import tools.refinery.viatra.runtime.matchers.util.Direction; - -/** - * @author Tamas Szabo - * - */ -public class CountingListener implements ITcObserver { - - private IncSCCAlg alg; - - public CountingListener(IncSCCAlg alg) { - this.alg = alg; - } - - @Override - public void tupleInserted(V source, V target) { - alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.INSERT); - } - - @Override - public void tupleDeleted(V source, V target) { - alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.DELETE); - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java deleted file mode 100644 index f1e0ad44..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java +++ /dev/null @@ -1,645 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.incscc; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.counting.CountingAlg; -import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; -import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.GraphHelper; -import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple; -import tools.refinery.viatra.runtime.base.itc.alg.misc.bfs.BFS; -import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; -import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCCResult; -import tools.refinery.viatra.runtime.base.itc.alg.util.CollectionHelper; -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; -import tools.refinery.viatra.runtime.matchers.algorithms.UnionFind; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.Direction; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -/** - * Incremental SCC maintenance + counting algorithm. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the nodes in the graph data source - */ -public class IncSCCAlg implements IGraphObserver, ITcDataSource { - - public UnionFind sccs; - public IBiDirectionalGraphDataSource gds; - private CountingAlg counting; - private Graph reducedGraph; - private IBiDirectionalGraphDataSource reducedGraphIndexer; - private List> observers; - private CountingListener countingListener; - - public IncSCCAlg(IGraphDataSource graphDataSource) { - - if (graphDataSource instanceof IBiDirectionalGraphDataSource) { - gds = (IBiDirectionalGraphDataSource) graphDataSource; - } else { - gds = new IBiDirectionalWrapper(graphDataSource); - } - observers = CollectionsFactory.createObserverList(); - sccs = new UnionFind(); - reducedGraph = new Graph(); - reducedGraphIndexer = new IBiDirectionalWrapper(reducedGraph); - countingListener = new CountingListener(this); - initalizeInternalDataStructures(); - gds.attachObserver(this); - } - - private void initalizeInternalDataStructures() { - SCCResult _sccres = SCC.computeSCC(gds); - Set> _sccs = _sccres.getSccs(); - - for (Set _set : _sccs) { - sccs.makeSet(_set); - } - - // Initalization of the reduced graph - for (V n : sccs.getPartitionHeads()) { - reducedGraph.insertNode(n); - } - - for (V source : gds.getAllNodes()) { - final IMemoryView targetNodes = gds.getTargetNodes(source); - for (Entry entry : targetNodes.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V target = entry.getKey(); - V sourceRoot = sccs.find(source); - V targetRoot = sccs.find(target); - - if (!sourceRoot.equals(targetRoot)) { - reducedGraph.insertEdge(sourceRoot, targetRoot); - } - } - } - } - - counting = new CountingAlg(reducedGraph); - } - - @Override - public void edgeInserted(V source, V target) { - V sourceRoot = sccs.find(source); - V targetRoot = sccs.find(target); - - // Different SCC - if (!sourceRoot.equals(targetRoot)) { - - // source is reachable from target? - if (counting.isReachable(targetRoot, sourceRoot)) { - - Set predecessorRoots = counting.getAllReachableSources(sourceRoot); - Set successorRoots = counting.getAllReachableTargets(targetRoot); - - // 1. intersection of source and target roots, these will be in the merged SCC - Set isectRoots = CollectionHelper.intersection(predecessorRoots, successorRoots); - isectRoots.add(sourceRoot); - isectRoots.add(targetRoot); - - // notifications must be issued before Union-Find modifications - if (observers.size() > 0) { - Set sourceSCCs = createSetNullTolerant(predecessorRoots); - sourceSCCs.add(sourceRoot); - Set targetSCCs = createSetNullTolerant(successorRoots); - targetSCCs.add(targetRoot); - - // tracing back to actual nodes - for (V sourceSCC : sourceSCCs) { - targetLoop: for (V targetSCC : targetSCCs) { - if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; - - boolean needsNotification = - // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. - // Issue notifications only if there is no self-loop present at the moment - (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper - .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) - || - // Case 2. sourceSCC and targetSCC are different sccs. - (!sourceSCC.equals(targetSCC)); - // if self loop is already present omit the notification - if (needsNotification) { - notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), - Direction.INSERT); - } - } - } - } - - // 2. delete edges, nodes - List sourceSCCs = new ArrayList(); - List targetSCCs = new ArrayList(); - - for (V r : isectRoots) { - List sourceSCCsOfSCC = getSourceSCCsOfSCC(r); - List targetSCCsOfSCC = getTargetSCCsOfSCC(r); - - for (V sourceSCC : sourceSCCsOfSCC) { - if (!sourceSCC.equals(r)) { - reducedGraph.deleteEdgeIfExists(sourceSCC, r); - } - } - - for (V targetSCC : targetSCCsOfSCC) { - if (!isectRoots.contains(targetSCC) && !r.equals(targetSCC)) { - reducedGraph.deleteEdgeIfExists(r, targetSCC); - } - } - - sourceSCCs.addAll(sourceSCCsOfSCC); - targetSCCs.addAll(targetSCCsOfSCC); - } - - for (V r : isectRoots) { - reducedGraph.deleteNode(r); - } - - // 3. union - Iterator iterator = isectRoots.iterator(); - V newRoot = iterator.next(); - while (iterator.hasNext()) { - newRoot = sccs.union(newRoot, iterator.next()); - } - - // 4. add new node - reducedGraph.insertNode(newRoot); - - // 5. add edges - Set containedNodes = sccs.getPartition(newRoot); - - for (V sourceSCC : sourceSCCs) { - if (!containedNodes.contains(sourceSCC) && !sourceSCC.equals(newRoot)) { - reducedGraph.insertEdge(sourceSCC, newRoot); - } - } - for (V targetSCC : targetSCCs) { - if (!containedNodes.contains(targetSCC) && !targetSCC.equals(newRoot)) { - reducedGraph.insertEdge(newRoot, targetSCC); - } - } - } else { - if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 1) { - counting.attachObserver(countingListener); - } - reducedGraph.insertEdge(sourceRoot, targetRoot); - counting.detachObserver(countingListener); - } - } else { - // Notifications about self-loops - if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 - && GraphHelper.getEdgeCount(source, target, gds) == 1) { - notifyTcObservers(source, source, Direction.INSERT); - } - } - } - - @Override - public void edgeDeleted(V source, V target) { - V sourceRoot = sccs.find(source); - V targetRoot = sccs.find(target); - - if (!sourceRoot.equals(targetRoot)) { - if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 0) { - counting.attachObserver(countingListener); - } - reducedGraph.deleteEdgeIfExists(sourceRoot, targetRoot); - counting.detachObserver(countingListener); - } else { - // get the graph for the scc whose root is sourceRoot - Graph g = GraphHelper.getSubGraph(sccs.getPartition(sourceRoot), gds); - - // if source is not reachable from target anymore - if (!BFS.isReachable(source, target, g)) { - // create copies of the current state before destructive manipulation - Map reachableSources = CollectionsFactory.createMap(); - for (Entry entry : reducedGraphIndexer.getSourceNodes(sourceRoot).entriesWithMultiplicities()) { - reachableSources.put(entry.getKey(), entry.getValue()); - } - Map reachableTargets = CollectionsFactory.createMap(); - for (Entry entry : reducedGraphIndexer.getTargetNodes(sourceRoot).entriesWithMultiplicities()) { - reachableTargets.put(entry.getKey(), entry.getValue()); - } - - SCCResult _newSccs = SCC.computeSCC(g); - - // delete scc node (and with its edges too) - for (Entry entry : reachableSources.entrySet()) { - V s = entry.getKey(); - for (int i = 0; i < entry.getValue(); i++) { - reducedGraph.deleteEdgeIfExists(s, sourceRoot); - } - } - - for (Entry entry : reachableTargets.entrySet()) { - V t = entry.getKey(); - for (int i = 0; i < entry.getValue(); i++) { - reducedGraph.deleteEdgeIfExists(sourceRoot, t); - } - } - - sccs.deleteSet(sourceRoot); - reducedGraph.deleteNode(sourceRoot); - - Set> newSCCs = _newSccs.getSccs(); - Set newSCCRoots = CollectionsFactory.createSet(); - - // add new nodes and edges to the reduced graph - for (Set newSCC : newSCCs) { - V newRoot = sccs.makeSet(newSCC); - reducedGraph.insertNode(newRoot); - newSCCRoots.add(newRoot); - } - for (V newSCCRoot : newSCCRoots) { - List sourceSCCsOfSCC = getSourceSCCsOfSCC(newSCCRoot); - List targetSCCsOfSCC = getTargetSCCsOfSCC(newSCCRoot); - - for (V sourceSCC : sourceSCCsOfSCC) { - if (!sourceSCC.equals(newSCCRoot)) { - reducedGraph.insertEdge(sccs.find(sourceSCC), newSCCRoot); - } - } - for (V targetSCC : targetSCCsOfSCC) { - if (!newSCCRoots.contains(targetSCC) && !targetSCC.equals(newSCCRoot)) - reducedGraph.insertEdge(newSCCRoot, targetSCC); - } - } - - // Must be after the union-find modifications - if (observers.size() > 0) { - V newSourceRoot = sccs.find(source); - V newTargetRoot = sccs.find(target); - - Set sourceSCCs = createSetNullTolerant(counting.getAllReachableSources(newSourceRoot)); - sourceSCCs.add(newSourceRoot); - - Set targetSCCs = createSetNullTolerant(counting.getAllReachableTargets(newTargetRoot)); - targetSCCs.add(newTargetRoot); - - for (V sourceSCC : sourceSCCs) { - targetLoop: for (V targetSCC : targetSCCs) { - if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; - - boolean needsNotification = - // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. - // Issue notifications only if there is no self-loop present at the moment - (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper - .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) - || - // Case 2. sourceSCC and targetSCC are different sccs. - (!sourceSCC.equals(targetSCC)); - // if self loop is already present omit the notification - if (needsNotification) { - notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), - Direction.DELETE); - } - } - } - } - } else { - // only handle self-loop notifications - sourceRoot equals to targetRoot - if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 - && GraphHelper.getEdgeCount(source, target, gds) == 0) { - notifyTcObservers(source, source, Direction.DELETE); - } - } - } - } - - @Override - public void nodeInserted(V n) { - sccs.makeSet(n); - reducedGraph.insertNode(n); - } - - @Override - public void nodeDeleted(V n) { - IMemoryView sources = gds.getSourceNodes(n); - IMemoryView targets = gds.getTargetNodes(n); - - for (Entry entry : sources.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V source = entry.getKey(); - edgeDeleted(source, n); - } - } - - for (Entry entry : targets.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V target = entry.getKey(); - edgeDeleted(n, target); - } - } - - sccs.deleteSet(n); - } - - @Override - public void attachObserver(ITcObserver to) { - observers.add(to); - } - - @Override - public void detachObserver(ITcObserver to) { - observers.remove(to); - } - - @Override - public Set getAllReachableTargets(V source) { - V sourceRoot = sccs.find(source); - Set containedNodes = sccs.getPartition(sourceRoot); - Set targets = CollectionsFactory.createSet(); - - if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(source, gds) == 1) { - targets.addAll(containedNodes); - } - - Set rootSet = counting.getAllReachableTargets(sourceRoot); - if (rootSet != null) { - for (V _root : rootSet) { - targets.addAll(sccs.getPartition(_root)); - } - } - - return targets; - } - - @Override - public Set getAllReachableSources(V target) { - V targetRoot = sccs.find(target); - Set containedNodes = sccs.getPartition(targetRoot); - Set sources = CollectionsFactory.createSet(); - - if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(target, gds) == 1) { - sources.addAll(containedNodes); - } - - Set rootSet = counting.getAllReachableSources(targetRoot); - if (rootSet != null) { - for (V _root : rootSet) { - sources.addAll(sccs.getPartition(_root)); - } - } - return sources; - } - - @Override - public boolean isReachable(V source, V target) { - V sourceRoot = sccs.find(source); - V targetRoot = sccs.find(target); - - if (sourceRoot.equals(targetRoot)) - return true; - else - return counting.isReachable(sourceRoot, targetRoot); - } - - public List getReachabilityPath(V source, V target) { - if (!isReachable(source, target)) { - return null; - } else { - Set sccsInSubGraph = CollectionHelper.intersection(counting.getAllReachableTargets(source), - counting.getAllReachableSources(target)); - sccsInSubGraph.add(sccs.find(source)); - sccsInSubGraph.add(sccs.find(target)); - Set nodesInSubGraph = CollectionsFactory.createSet(); - - for (V sccRoot : sccsInSubGraph) { - nodesInSubGraph.addAll(sccs.getPartition(sccRoot)); - } - - return GraphHelper.constructPath(source, target, nodesInSubGraph, gds); - } - } - - // for JUnit - public boolean checkTcRelation(DRedTcRelation tc) { - - for (V s : tc.getTupleStarts()) { - for (V t : tc.getTupleEnds(s)) { - if (!isReachable(s, t)) - return false; - } - } - - for (V root : counting.getTcRelation().getTupleStarts()) { - for (V end : counting.getTcRelation().getTupleEnds(root)) { - for (V s : sccs.getPartition(root)) { - for (V t : sccs.getPartition(end)) { - if (!tc.containsTuple(s, t)) - return false; - } - } - } - } - - return true; - } - - /** - * Return the SCCs from which the SCC represented by the root node is reachable. Note that an SCC can be present - * multiple times in the returned list (multiple edges between the two SCCs). - * - * @param root - * @return the list of reachable target SCCs - */ - private List getSourceSCCsOfSCC(V root) { - List sourceSCCs = new ArrayList(); - - for (V containedNode : this.sccs.getPartition(root)) { - IMemoryView sourceNodes = this.gds.getSourceNodes(containedNode); - for (V source : sourceNodes.distinctValues()) { - sourceSCCs.add(this.sccs.find(source)); - } - } - - return sourceSCCs; - } - - /** - * Returns true if the SCC represented by the given root node has incoming edges in the reduced graph, - * false otherwise (if this SCC is a source in the reduced graph). - * - * @param root the root node of an SCC - * @return true if it has incoming edges, false otherwise - * @since 1.6 - */ - public boolean hasIncomingEdges(final V root) { - for (final V containedNode : this.sccs.getPartition(root)) { - final IMemoryView sourceNodes = this.gds.getSourceNodes(containedNode); - for (final V source : sourceNodes.distinctValues()) { - final V otherRoot = this.sccs.find(source); - if (!Objects.equals(root, otherRoot)) { - return true; - } - } - } - return false; - } - - /** - * Return the SCCs which are reachable from the SCC represented by the root node. Note that an SCC can be present - * multiple times in the returned list (multiple edges between the two SCCs). - * - * @param root - * @return the list of reachable target SCCs - */ - private List getTargetSCCsOfSCC(V root) { - List targetSCCs = new ArrayList(); - - for (V containedNode : this.sccs.getPartition(root)) { - IMemoryView targetNodes = this.gds.getTargetNodes(containedNode); - for (V target : targetNodes.distinctValues()) { - targetSCCs.add(this.sccs.find(target)); - } - } - - return targetSCCs; - } - - /** - * Returns true if the SCC represented by the given root node has outgoing edges in the reduced graph, - * false otherwise (if this SCC is a sink in the reduced graph). - * - * @param root the root node of an SCC - * @return true if it has outgoing edges, false otherwise - * @since 1.6 - */ - public boolean hasOutgoingEdges(V root) { - for (final V containedNode : this.sccs.getPartition(root)) { - final IMemoryView targetNodes = this.gds.getTargetNodes(containedNode); - for (final V target : targetNodes.distinctValues()) { - final V otherRoot = this.sccs.find(target); - if (!Objects.equals(root, otherRoot)) { - return true; - } - } - } - return false; - } - - @Override - public void dispose() { - gds.detachObserver(this); - counting.dispose(); - } - - /** - * Call this method to notify the observers of the transitive closure relation. The tuples used in the notification - * will be the Descartes product of the two sets given. - * - * @param sources - * the source nodes - * @param targets - * the target nodes - * @param direction - */ - protected void notifyTcObservers(Set sources, Set targets, Direction direction) { - for (V s : sources) { - for (V t : targets) { - notifyTcObservers(s, t, direction); - } - } - } - - private void notifyTcObservers(V source, V target, Direction direction) { - for (ITcObserver observer : observers) { - if (direction == Direction.INSERT) { - observer.tupleInserted(source, target); - } - if (direction == Direction.DELETE) { - observer.tupleDeleted(source, target); - } - } - } - - /** - * Returns the node that is selected as the representative of the SCC containing the argument. - * @since 1.6 - */ - public V getRepresentative(V node) { - return sccs.find(node); - } - - public Set> getTcRelation() { - Set> resultSet = new HashSet>(); - - for (V sourceRoot : sccs.getPartitionHeads()) { - Set sources = sccs.getPartition(sourceRoot); - if (sources.size() > 1 || GraphHelper.getEdgeCount(sources.iterator().next(), gds) == 1) { - for (V source : sources) { - for (V target : sources) { - resultSet.add(new Tuple(source, target)); - } - } - } - - Set reachableTargets = counting.getAllReachableTargets(sourceRoot); - if (reachableTargets != null) { - for (V targetRoot : reachableTargets) { - for (V source : sources) { - for (V target : sccs.getPartition(targetRoot)) { - resultSet.add(new Tuple(source, target)); - } - } - } - } - } - - return resultSet; - } - - public boolean isIsolated(V node) { - IMemoryView targets = gds.getTargetNodes(node); - IMemoryView sources = gds.getSourceNodes(node); - return targets.isEmpty() && sources.isEmpty(); - } - - @Override - public IGraphPathFinder getPathFinder() { - return new DFSPathFinder(gds, this); - } - - /** - * The graph of SCCs; each SCC is represented by its representative node (see {@link #getRepresentative(Object)}) - * @since 1.6 - */ - public Graph getReducedGraph() { - return reducedGraph; - } - - private static Set createSetNullTolerant(Set initial) { - if (initial != null) - return CollectionsFactory.createSet(initial); - else - return CollectionsFactory.createSet(); - } - - -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java deleted file mode 100644 index 51017b1a..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Abel Hegedus and IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -/** - * A depth-first search implementation of the {@link IGraphPathFinder}. - * - * TODO use ITC to filter nodes that must be traversed, instead of checks - * - * @author Abel Hegedus - * - * @param - * the node type of the graph - */ -public class DFSPathFinder implements IGraphPathFinder { - - private IGraphDataSource graph; - private ITcDataSource itc; - - public DFSPathFinder(IGraphDataSource graph, ITcDataSource itc) { - this.graph = graph; - this.itc = itc; - } - - @Override - public Iterable> getAllPaths(V sourceNode, V targetNode) { - Set endNodes = new HashSet(); - endNodes.add(targetNode); - return getAllPathsToTargets(sourceNode, endNodes); - } - - @Override - public Iterable> getAllPathsToTargets(V sourceNode, Set targetNodes) { - List> paths = new ArrayList>(); - Deque visited = new LinkedList(); - Set reachableTargets = new HashSet(); - for (V targetNode : targetNodes) { - if (itc.isReachable(sourceNode, targetNode)) { - reachableTargets.add(targetNode); - } - } - if (!reachableTargets.isEmpty()) { - return paths; - } - visited.add(sourceNode); - return getPaths(paths, visited, reachableTargets); - } - - protected Iterable> getPaths(List> paths, Deque visited, Set targetNodes) { - IMemoryView nodes = graph.getTargetNodes(visited.getLast()); - // examine adjacent nodes - for (V node : nodes.distinctValues()) { - if (visited.contains(node)) { - continue; - } - if (targetNodes.contains(node)) { - visited.add(node); - // clone visited LinkedList - Deque visitedClone = new LinkedList(visited); - paths.add(visitedClone); - visited.removeLast(); - break; - } - } - - // in breadth-first, recursion needs to come after visiting connected nodes - for (V node : nodes.distinctValues()) { - if (visited.contains(node) || targetNodes.contains(node)) { - continue; - } - boolean canReachTarget = false; - for (V target : targetNodes) { - if (itc.isReachable(node, target)) { - canReachTarget = true; - break; - } - } - if (canReachTarget) { - visited.addLast(node); - getPaths(paths, visited, targetNodes); - visited.removeLast(); - } - } - - return paths; - } - - public String printPaths(List> paths) { - StringBuilder sb = new StringBuilder(); - for (Deque visited : paths) { - sb.append("Path: "); - for (V node : visited) { - sb.append(node); - sb.append(" --> "); - } - sb.append("\n"); - } - return sb.toString(); - } - - @Override - public Deque getPath(V sourceNode, V targetNode) { - // TODO optimize - Iterable> allPaths = getAllPaths(sourceNode, targetNode); - Iterator> pathIterator = allPaths.iterator(); - return pathIterator.hasNext() ? pathIterator.next() : new LinkedList(); - } - - @Override - public Iterable> getShortestPaths(V sourceNode, V targetNode) { - // TODO optimize - Iterable> allPaths = getAllPaths(sourceNode, targetNode); - List> shortestPaths = new ArrayList>(); - int shortestPathLength = -1; - for (Deque path : allPaths) { - int pathLength = path.size(); - if (shortestPathLength == -1 || pathLength < shortestPathLength) { - shortestPaths.clear(); - shortestPathLength = pathLength; - } - if (pathLength == shortestPathLength) { - shortestPaths.add(path); - } - } - return shortestPaths; - } - -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java deleted file mode 100644 index cf68d36a..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -public class Edge { - private V source; - private V target; - - public Edge(V source, V target) { - super(); - this.source = source; - this.target = target; - } - - public V getSource() { - return source; - } - - public void setSource(V source) { - this.source = source; - } - - public V getTarget() { - return target; - } - - public void setTarget(V target) { - this.target = target; - } - -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java deleted file mode 100644 index 194e979b..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java +++ /dev/null @@ -1,173 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -/** - * Utility class for graph related operations. - * - * @author Tamas Szabo - */ -public class GraphHelper { - - private GraphHelper() {/*Utility class constructor*/} - - /** - * Returns the subgraph from the given {@link IBiDirectionalGraphDataSource} which contains the given set of nodes. - * - * @param nodesInSubGraph - * the nodes that are present in the subgraph - * @param graphDataSource - * the graph data source for the original graph - * @return the subgraph associated to the given nodes - */ - public static Graph getSubGraph(Collection nodesInSubGraph, - IBiDirectionalGraphDataSource graphDataSource) { - Graph g = new Graph(); - if (nodesInSubGraph != null) { - for (V node : nodesInSubGraph) { - g.insertNode(node); - } - - for (V node : nodesInSubGraph) { - IMemoryView sources = graphDataSource.getSourceNodes(node); - for (Entry entry : sources.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V s = entry.getKey(); - if (nodesInSubGraph.contains(s)) { - g.insertEdge(s, node); - } - } - } - } - } - - return g; - } - - /** - * Constructs a path between source and target in the given graph. Both the {@link IGraphDataSource} and the set of - * nodes are used, this way it is possible to construct a path in a given subgraph. - * - * The returned {@link List} contains the nodes along the path (this means that there is an edge in the graph - * between two consecutive nodes). A self loop (one edge) is indicated with the source node being present two times - * in the returned {@link List}. - * - * @param source - * the source node - * @param target - * the target node - * @param nodesInGraph - * the nodes that are present in the subgraph - * @param graphDataSource - * the graph data source - * @return the path between the two nodes - */ - public static List constructPath(V source, V target, Set nodesInGraph, - IGraphDataSource graphDataSource) { - Set visitedNodes = new HashSet(); - List path = new ArrayList(); - - visitedNodes.add(source); - path.add(source); - V act = source; - - // if source and target are the same node - if (source.equals(target) && graphDataSource.getTargetNodes(source).containsNonZero(target)) { - // the node will be present in the path two times - path.add(source); - return path; - } else { - while (act != null) { - V nextNode = getNextNodeToVisit(act, graphDataSource, nodesInGraph, visitedNodes); - if (nextNode == null && path.size() > 1) { - // needs to backtrack along path - // remove the last element in the path because we can't go - // anywhere from there - path.remove(path.size() - 1); - while (nextNode == null && path.size() > 0) { - V lastPathElement = path.get(path.size() - 1); - nextNode = getNextNodeToVisit(lastPathElement, graphDataSource, nodesInGraph, visitedNodes); - if (nextNode == null) { - path.remove(path.size() - 1); - } - } - } - - if (nextNode != null) { - visitedNodes.add(nextNode); - path.add(nextNode); - if (nextNode.equals(target)) { - return path; - } - } - act = nextNode; - } - return null; - } - } - - private static V getNextNodeToVisit(V act, IGraphDataSource graphDataSource, Set nodesInSubGraph, - Set visitedNodes) { - IMemoryView targetNodes = graphDataSource.getTargetNodes(act); - for (Entry entry : targetNodes.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V node = entry.getKey(); - if (nodesInSubGraph.contains(node) && !visitedNodes.contains(node)) { - return node; - } - } - } - return null; - } - - /** - * Returns the number of self-loop edges for the given node. - * - * @param node - * the node - * @param graphDataSource - * the graph data source - * @return the number of self-loop edges - */ - public static int getEdgeCount(V node, IGraphDataSource graphDataSource) { - return getEdgeCount(node, node, graphDataSource); - } - - /** - * Returns the number of edges between the given source and target nodes. - * - * @param source - * the source node - * @param target - * the target node - * @param graphDataSource - * the graph data source - * @return the number of parallel edges between the two nodes - */ - public static int getEdgeCount(V source, V target, IGraphDataSource graphDataSource) { - Integer count = graphDataSource.getTargetNodes(source).getCount(target); - if (count == null) { - return 0; - } else { - return count; - } - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java deleted file mode 100644 index cebb09c8..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -import java.util.Deque; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; - -/** - * The path finder provides methods for retrieving paths in a graph between a source node and one or more target nodes. - * Use {@link ITcDataSource#getPathFinder()} for instantiating. - * - * @author Abel Hegedus - * - * @param the node type of the graph - */ -public interface IGraphPathFinder { - - /** - * Returns an arbitrary path from the source node to the target node (if such exists). If there is no path - * between them, an empty collection is returned. - * - * @param sourceNode the source node of the path - * @param targetNode the target node of the path - * @return the path from the source to the target, or empty collection if target is not reachable from source. - */ - Deque getPath(V sourceNode, V targetNode); - - /** - * Returns the collection of shortest paths from the source node to the target node (if such exists). If there is no path - * between them, an empty collection is returned. - * - * @param sourceNode the source node of the path - * @param targetNode the target node of the path - * @return the collection of shortest paths from the source to the target, or empty collection if target is not reachable from source. - */ - Iterable> getShortestPaths(V sourceNode, V targetNode); - - /** - * Returns the collection of paths from the source node to the target node (if such exists). If there is no path - * between them, an empty collection is returned. - * - * @param sourceNode the source node of the path - * @param targetNode the target node of the path - * @return the collection of paths from the source to the target, or empty collection if target is not reachable from source. - */ - Iterable> getAllPaths(V sourceNode, V targetNode); - - /** - * Returns the collection of paths from the source node to any of the target nodes (if such exists). If there is no path - * between them, an empty collection is returned. - * - * @param sourceNode the source node of the path - * @param targetNodes the set of target nodes of the paths - * @return the collection of paths from the source to any of the targets, or empty collection if neither target is reachable from source. - */ - Iterable> getAllPathsToTargets(V sourceNode, Set targetNodes); - - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java deleted file mode 100644 index a41ff6c7..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -import java.util.Set; - -public interface ITcRelation { - - /** - * Returns the starting nodes from a transitive closure relation. - * - * @return the set of starting nodes - */ - public Set getTupleStarts(); - - /** - * Returns the set of nodes that are reachable from the given node. - * - * @param start - * the starting node - * @return the set of reachable nodes - */ - public Set getTupleEnds(V start); -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java deleted file mode 100644 index a2d54a59..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -public class Tuple { - - private V source; - private V target; - - public Tuple(V source, V target) { - super(); - this.source = source; - this.target = target; - } - - public V getSource() { - return source; - } - - public void setSource(V source) { - this.source = source; - } - - public V getTarget() { - return target; - } - - public void setTarget(V target) { - this.target = target; - } - - @Override - public String toString() { - return "(" + source.toString() + "," + target.toString() + ")"; - } - - @Override - public boolean equals(Object o) { - if (o instanceof Tuple) { - Tuple t = (Tuple) o; - - if (t.getSource().equals(this.source) && t.getTarget().equals(this.target)) { - return true; - } - } - return false; - } - - @Override - public int hashCode() { - return source.hashCode() + target.hashCode(); - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java deleted file mode 100644 index 798f31d2..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java +++ /dev/null @@ -1,151 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.bfs; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; - -public class BFS { - - private BFS() {/*Utility class constructor*/} - - /** - * Performs a breadth first search on the given graph to determine whether source is reachable from target. - * - * @param - * the type parameter of the nodes in the graph - * @param source - * the source node - * @param target - * the target node - * @param graph - * the graph data source - * @return true if source is reachable from target, false otherwise - */ - public static boolean isReachable(V source, V target, IGraphDataSource graph) { - List nodeQueue = new ArrayList(); - Set visited = new HashSet(); - - nodeQueue.add(source); - visited.add(source); - - boolean ret = _isReachable(target, graph, nodeQueue, visited); - return ret; - } - - private static boolean _isReachable(V target, IGraphDataSource graph, List nodeQueue, Set visited) { - - while (!nodeQueue.isEmpty()) { - V node = nodeQueue.remove(0); - for (V t : graph.getTargetNodes(node).distinctValues()){ - if (t.equals(target)) { - return true; - } - if (!visited.contains(t)) { - visited.add(t); - nodeQueue.add(t); - } - } - } - return false; - } - - public static Set reachableSources(IBiDirectionalGraphDataSource graph, V target) { - Set retSet = new HashSet(); - retSet.add(target); - List nodeQueue = new ArrayList(); - nodeQueue.add(target); - - _reachableSources(graph, nodeQueue, retSet); - - return retSet; - } - - private static void _reachableSources(IBiDirectionalGraphDataSource graph, List nodeQueue, - Set retSet) { - while (!nodeQueue.isEmpty()) { - V node = nodeQueue.remove(0); - for (V _node : graph.getSourceNodes(node).distinctValues()) { - if (!retSet.contains(_node)) { - retSet.add(_node); - nodeQueue.add(_node); - } - } - } - } - - public static Set reachableTargets(IGraphDataSource graph, V source) { - Set retSet = new HashSet(); - retSet.add(source); - List nodeQueue = new ArrayList(); - nodeQueue.add(source); - - _reachableTargets(graph, nodeQueue, retSet); - - return retSet; - } - - private static void _reachableTargets(IGraphDataSource graph, List nodeQueue, Set retSet) { - while (!nodeQueue.isEmpty()) { - V node = nodeQueue.remove(0); - - for (V _node : graph.getTargetNodes(node).distinctValues()) { - - if (!retSet.contains(_node)) { - retSet.add(_node); - nodeQueue.add(_node); - } - } - } - } - - /** - * Performs a breadth first search on the given graph and collects all the nodes along the path from source to - * target if such path exists. - * - * @param - * the type parameter of the nodes in the graph - * @param source - * the source node - * @param target - * the target node - * @param graph - * the graph data source - * @return the set of nodes along the path - */ - public static Set collectNodesAlongPath(V source, V target, IGraphDataSource graph) { - Set path = new HashSet(); - _collectNodesAlongPath(source, target, graph, path); - return path; - } - - private static boolean _collectNodesAlongPath(V node, V target, IGraphDataSource graph, Set path) { - - boolean res = false; - - // end recursion - if (node.equals(target)) { - path.add(node); - return true; - } else { - for (V _nodeT : graph.getTargetNodes(node).distinctValues()) { - res = (_collectNodesAlongPath(_nodeT, target, graph, path)) || res; - } - if (res) - path.add(node); - return res; - } - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java deleted file mode 100644 index c8d25c4e..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.dfs; - -import java.util.HashMap; - -import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; - -public class DFSAlg implements IGraphObserver { - - private IGraphDataSource gds; - private DRedTcRelation tc; - private int[] visited; - private HashMap nodeMap; - - public DFSAlg(IGraphDataSource gds) { - this.gds = gds; - this.tc = new DRedTcRelation(); - gds.attachObserver(this); - deriveTc(); - } - - private void deriveTc() { - tc.clear(); - - this.visited = new int[gds.getAllNodes().size()]; - nodeMap = new HashMap(); - - int j = 0; - for (V n : gds.getAllNodes()) { - nodeMap.put(n, j); - j++; - } - - for (V n : gds.getAllNodes()) { - oneDFS(n, n); - initVisitedArray(); - } - } - - private void initVisitedArray() { - for (int i = 0; i < visited.length; i++) - visited[i] = 0; - } - - private void oneDFS(V act, V source) { - - if (!act.equals(source)) { - tc.addTuple(source, act); - } - - visited[nodeMap.get(act)] = 1; - - for (V t : gds.getTargetNodes(act).distinctValues()) { - if (visited[nodeMap.get(t)] == 0) { - oneDFS(t, source); - } - } - } - - public DRedTcRelation getTcRelation() { - return this.tc; - } - - @Override - public void edgeInserted(V source, V target) { - deriveTc(); - } - - @Override - public void edgeDeleted(V source, V target) { - deriveTc(); - } - - @Override - public void nodeInserted(V n) { - deriveTc(); - } - - @Override - public void nodeDeleted(V n) { - deriveTc(); - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java deleted file mode 100644 index c99a48ab..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java +++ /dev/null @@ -1,179 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; - -public class PKAlg implements IGraphObserver { - - /** - * Maps the nodes to their indicies. - */ - private Map node2index; - private Map index2node; - private Map node2mark; - - /** - * Maps the index of a node to the index in the topsort. - */ - private Map index2topsort; - private Map topsort2index; - - /** - * Index associated to the inserted nodes (incrementing with every insertion). - */ - private int index; - - /** - * Index within the topsort for the target node when edge insertion occurs. - */ - private int lower_bound; - - /** - * Index within the topsort for the source node when edge insertion occurs. - */ - private int upper_bound; - - private List RF; - private List RB; - private IBiDirectionalGraphDataSource gds; - - public PKAlg(IGraphDataSource gds) { - if (gds instanceof IBiDirectionalGraphDataSource) { - this.gds = (IBiDirectionalGraphDataSource) gds; - } else { - this.gds = new IBiDirectionalWrapper(gds); - } - - node2mark = new HashMap(); - node2index = new HashMap(); - index2node = new HashMap(); - index2topsort = new HashMap(); - topsort2index = new HashMap(); - index = 0; - - gds.attachObserver(this); - } - - @Override - public void edgeInserted(V source, V target) { - - RF = new ArrayList(); - RB = new ArrayList(); - - lower_bound = index2topsort.get(node2index.get(target)); - upper_bound = index2topsort.get(node2index.get(source)); - - if (lower_bound < upper_bound) { - dfsForward(target); - dfsBackward(source); - reorder(); - } - } - - private List getIndicies(List list) { - List indicies = new ArrayList(); - - for (V n : list) - indicies.add(index2topsort.get(node2index.get(n))); - - return indicies; - } - - private void reorder() { - - Collections.reverse(RB); - - // azon csomopontok indexei amelyek sorrendje nem jo - List L = getIndicies(RF); - L.addAll(getIndicies(RB)); - Collections.sort(L); - - for (int i = 0; i < RB.size(); i++) { - index2topsort.put(node2index.get(RB.get(i)), L.get(i)); - topsort2index.put(L.get(i), node2index.get(RB.get(i))); - } - - for (int i = 0; i < RF.size(); i++) { - index2topsort.put(node2index.get(RF.get(i)), L.get(i + RB.size())); - topsort2index.put(L.get(i + RB.size()), node2index.get(RF.get(i))); - } - } - - @SuppressWarnings("unused") - private List getTopSort() { - List topsort = new ArrayList(); - - for (int i : topsort2index.values()) { - topsort.add(index2node.get(i)); - } - - return topsort; - } - - private void dfsBackward(V node) { - node2mark.put(node, true); - RB.add(node); - - for (V sn : gds.getSourceNodes(node).distinctValues()) { - int top_id = index2topsort.get(node2index.get(sn)); - - if (!node2mark.get(sn) && lower_bound < top_id) - dfsBackward(sn); - } - } - - private void dfsForward(V node) { - node2mark.put(node, true); - RF.add(node); - - for (V tn : gds.getTargetNodes(node).distinctValues()) { - int top_id = index2topsort.get(node2index.get(tn)); - - if (top_id == upper_bound) - System.out.println("!!!Cycle detected!!!"); - else if (!node2mark.get(tn) && top_id < upper_bound) - dfsForward(tn); - } - } - - @Override - public void edgeDeleted(V source, V target) { - // Edge deletion does not affect topsort - } - - @Override - public void nodeInserted(V n) { - node2mark.put(n, false); - node2index.put(n, index); - index2node.put(index, n); - index2topsort.put(index, index); - topsort2index.put(index, index); - index++; - } - - @Override - public void nodeDeleted(V n) { - node2mark.remove(n); - int node_id = node2index.remove(n); - index2node.remove(node_id); - int top_id = index2topsort.remove(node_id); - topsort2index.remove(top_id); - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java deleted file mode 100644 index 8915998b..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; - -/** - * Efficient algorithms to compute the Strongly Connected Components in a directed graph. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the nodes in the graph - */ -public class SCC { - - private SCC() {/*Utility class constructor*/} - - public static long sccId = 0; - - /** - * Computes the SCCs for the given graph and returns them as a multiset. (Iterative version of Tarjan's algorithm) - * - * @param g - * the directed graph data source - * @return the set of SCCs - */ - public static SCCResult computeSCC(IGraphDataSource g) { - int index = 0; - Set> ret = new HashSet>(); - - // stores the lowlink and index information for the given node - Map nodeMap = CollectionsFactory.createMap(); - - // stores all target nodes of a given node - the list will be modified - Map> targetNodeMap = CollectionsFactory.createMap(); - - // stores those target nodes for a given node which have not been visited - Map> notVisitedMap = CollectionsFactory.createMap(); - - // stores the nodes during the traversal - Stack nodeStack = new Stack(); - - // stores the nodes which belong to an scc (there can be many sccs in the stack at the same time) - Stack sccStack = new Stack(); - - boolean sink = false, finishedTraversal = true; - - // initialize all nodes with 0 index and 0 lowlink - Set allNodes = g.getAllNodes(); - for (V n : allNodes) { - nodeMap.put(n, new SCCProperty(0, 0)); - } - - for (V n : allNodes) { - // if the node has not been visited yet - if (nodeMap.get(n).getIndex() == 0) { - nodeStack.push(n); - - while (!nodeStack.isEmpty()) { - V currentNode = nodeStack.peek(); - sink = false; - finishedTraversal = false; - SCCProperty prop = nodeMap.get(currentNode); - - if (nodeMap.get(currentNode).getIndex() == 0) { - index++; - sccStack.push(currentNode); - prop.setIndex(index); - prop.setLowlink(index); - - notVisitedMap.put(currentNode, new HashSet()); - - // storing the target nodes of the actual node - if (g.getTargetNodes(currentNode) != null) { - Set targets = g.getTargetNodes(currentNode).distinctValues(); - targetNodeMap.put(currentNode, CollectionsFactory.createSet(targets)); - } - } - - if (targetNodeMap.get(currentNode) != null) { - - // remove node from stack, the exploration of its children has finished - if (targetNodeMap.get(currentNode).size() == 0) { - targetNodeMap.remove(currentNode); - - nodeStack.pop(); - - for (V targetNode : g.getTargetNodes(currentNode).distinctValues()) { - if (notVisitedMap.get(currentNode).contains(targetNode)) { - prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getLowlink())); - } else if (sccStack.contains(targetNode)) { - prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getIndex())); - } - } - - finishedTraversal = true; - } else { - V targetNode = targetNodeMap.get(currentNode).iterator().next(); - targetNodeMap.get(currentNode).remove(targetNode); - // if the targetNode has not yet been visited push it to the stack - // and mark it in the notVisitedMap - if (nodeMap.get(targetNode).getIndex() == 0) { - notVisitedMap.get(currentNode).add(targetNode); - nodeStack.add(targetNode); - } - } - } - // if currentNode has no target nodes - else { - nodeStack.pop(); - sink = true; - } - - // create scc if node is a sink or an scc has been found - if ((sink || finishedTraversal) && (prop.getLowlink() == prop.getIndex())) { - Set sc = new HashSet(); - V targetNode = null; - - do { - targetNode = sccStack.pop(); - sc.add(targetNode); - } while (!targetNode.equals(currentNode)); - - ret.add(sc); - } - } - } - } - - return new SCCResult(ret, g); - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java deleted file mode 100644 index b26e3d37..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; - -public class SCCProperty { - private int index; - private int lowlink; - - public SCCProperty(int index, int lowlink) { - super(); - this.index = index; - this.lowlink = lowlink; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - public int getLowlink() { - return lowlink; - } - - public void setLowlink(int lowlink) { - this.lowlink = lowlink; - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java deleted file mode 100644 index fde59d3b..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; - -import java.util.Map.Entry; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; - -public class SCCResult { - - private Set> sccs; - private IGraphDataSource gds; - - public SCCResult(Set> sccs, IGraphDataSource gds) { - this.sccs = sccs; - this.gds = gds; - } - - public Set> getSccs() { - return sccs; - } - - public int getSCCCount() { - return sccs.size(); - } - - public double getAverageNodeCount() { - double a = 0; - - for (Set s : sccs) { - a += s.size(); - } - - return a / sccs.size(); - } - - public double getAverageEdgeCount() { - long edgeSum = 0; - - for (Set scc : sccs) { - for (V source : scc) { - for (Entry entry : gds.getTargetNodes(source).entriesWithMultiplicities()) { - if (scc.contains(entry.getKey())) { - edgeSum += entry.getValue(); - } - } - } - } - - return (double) edgeSum / (double) sccs.size(); - } - - public int getBiggestSCCSize() { - int max = 0; - - for (Set scc : sccs) { - if (scc.size() > max) - max = scc.size(); - } - - return max; - } - - public long getSumOfSquares() { - long sum = 0; - - for (Set scc : sccs) { - sum += scc.size() * scc.size(); - } - - return sum; - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java deleted file mode 100644 index dd18e6c8..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java +++ /dev/null @@ -1,77 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.topsort; - -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.Stack; - -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; - -/** - * @since 1.6 - */ -public class TopologicalSorting { - - private TopologicalSorting() {/*Utility class constructor*/} - - private static final class Pair { - public T element; - public boolean isParent; - - public Pair(final T element, final boolean isParent) { - this.element = element; - this.isParent = isParent; - } - } - - /** - * Returns a topological ordering for the given graph data source. - * Output format: if there is an a -> b (transitive) reachability, then node a will come before node b in the resulting list. - * - * @param gds the graph data source - * @return a topological ordering - */ - public static List compute(final IGraphDataSource gds) { - final Set visited = new HashSet(); - final LinkedList result = new LinkedList(); - final Stack> dfsStack = new Stack>(); - - for (final T node : gds.getAllNodes()) { - if (!visited.contains(node)) { - dfsStack.push(new Pair(node, false)); - } - - while (!dfsStack.isEmpty()) { - final Pair head = dfsStack.pop(); - final T source = head.element; - - if (head.isParent) { - // we have already seen source, push it to the resulting stack - result.addFirst(source); - } else { - // first time we see source, continue with its children - visited.add(source); - dfsStack.push(new Pair(source, true)); - - for (final T target : gds.getTargetNodes(source).distinctValues()) { - if (!visited.contains(target)) { - dfsStack.push(new Pair(target, false)); - } - } - } - } - } - - return result; - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java deleted file mode 100644 index 51015404..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.viatra.runtime.base.itc.alg.representative; - -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.matchers.util.Direction; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public abstract class RepresentativeElectionAlgorithm implements IGraphObserver { - protected final Graph graph; - protected final Map representatives = new HashMap<>(); - protected final Map> components = new HashMap<>(); - private RepresentativeObserver observer; - - protected RepresentativeElectionAlgorithm(Graph graph) { - this.graph = graph; - initializeComponents(); - graph.attachObserver(this); - } - - protected abstract void initializeComponents(); - - protected void initializeSet(Set set) { - var iterator = set.iterator(); - if (!iterator.hasNext()) { - // Set is empty. - return; - } - var representative = iterator.next(); - for (var node : set) { - var oldRepresentative = representatives.put(node, representative); - if (oldRepresentative != null && !representative.equals(oldRepresentative)) { - throw new IllegalStateException("Node %s is already in a set represented by %s, cannot add it to %s" - .formatted(node, oldRepresentative, set)); - } - } - components.put(representative, set); - } - - protected void merge(Object leftRepresentative, Object rightRepresentative) { - if (leftRepresentative.equals(rightRepresentative)) { - return; - } - var leftSet = getComponent(leftRepresentative); - var rightSet = getComponent(rightRepresentative); - if (leftSet.size() < rightSet.size()) { - merge(rightRepresentative, rightSet, leftRepresentative, leftSet); - } else { - merge(leftRepresentative, leftSet, rightRepresentative, rightSet); - } - } - - private void merge(Object preservedRepresentative, Set preservedSet, Object removedRepresentative, - Set removedSet) { - components.remove(removedRepresentative); - for (var node : removedSet) { - representatives.put(node, preservedRepresentative); - preservedSet.add(node); - notifyToObservers(node, removedRepresentative, preservedRepresentative); - } - } - - protected void assignNewRepresentative(Object oldRepresentative, Set set) { - var iterator = set.iterator(); - if (!iterator.hasNext()) { - return; - } - var newRepresentative = iterator.next(); - components.put(newRepresentative, set); - for (var node : set) { - var oldRepresentativeOfNode = representatives.put(node, newRepresentative); - if (!oldRepresentative.equals(oldRepresentativeOfNode)) { - throw new IllegalArgumentException("Node %s was not represented by %s but by %s" - .formatted(node, oldRepresentative, oldRepresentativeOfNode)); - } - notifyToObservers(node, oldRepresentative, newRepresentative); - } - } - - public void setObserver(RepresentativeObserver observer) { - this.observer = observer; - } - - public Map> getComponents() { - return components; - } - - public Object getRepresentative(Object node) { - return representatives.get(node); - } - - public Set getComponent(Object representative) { - return components.get(representative); - } - - public void dispose() { - graph.detachObserver(this); - } - - @Override - public void nodeInserted(Object n) { - var component = new HashSet<>(1); - component.add(n); - initializeSet(component); - notifyToObservers(n, n, Direction.INSERT); - } - - @Override - public void nodeDeleted(Object n) { - var representative = representatives.remove(n); - if (!representative.equals(n)) { - throw new IllegalStateException("Trying to delete node with dangling edges"); - } - components.remove(representative); - notifyToObservers(n, representative, Direction.DELETE); - } - - protected void notifyToObservers(Object node, Object oldRepresentative, Object newRepresentative) { - notifyToObservers(node, oldRepresentative, Direction.DELETE); - notifyToObservers(node, newRepresentative, Direction.INSERT); - } - - protected void notifyToObservers(Object node, Object representative, Direction direction) { - if (observer != null) { - observer.tupleChanged(node, representative, direction); - } - } - - public interface Factory { - RepresentativeElectionAlgorithm create(Graph graph); - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java deleted file mode 100644 index 93cce1ea..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.viatra.runtime.base.itc.alg.representative; - -import tools.refinery.viatra.runtime.matchers.util.Direction; - -public interface RepresentativeObserver { - void tupleChanged(Object node, Object representative, Direction direction); -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java deleted file mode 100644 index ba42bb13..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.viatra.runtime.base.itc.alg.representative; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.GraphHelper; -import tools.refinery.viatra.runtime.base.itc.alg.misc.bfs.BFS; -import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; - -import java.util.Collection; -import java.util.Set; - -public class StronglyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { - public StronglyConnectedComponentAlgorithm(Graph graph) { - super(graph); - } - - @Override - protected void initializeComponents() { - var computedSCCs = SCC.computeSCC(graph).getSccs(); - for (var computedSCC : computedSCCs) { - initializeSet(computedSCC); - } - } - - @Override - public void edgeInserted(Object source, Object target) { - var sourceRoot = getRepresentative(source); - var targetRoot = getRepresentative(target); - if (sourceRoot.equals(targetRoot)) { - // New edge does not change strongly connected components. - return; - } - if (BFS.isReachable(target, source, graph)) { - merge(sourceRoot, targetRoot); - } - } - - @Override - public void edgeDeleted(Object source, Object target) { - var sourceRoot = getRepresentative(source); - var targetRoot = getRepresentative(target); - if (!sourceRoot.equals(targetRoot)) { - // New edge does not change strongly connected components. - return; - } - var component = GraphHelper.getSubGraph(getComponent(sourceRoot), graph); - if (!BFS.isReachable(source, target, component)) { - var newSCCs = SCC.computeSCC(component).getSccs(); - split(sourceRoot, newSCCs); - } - } - - private void split(Object preservedRepresentative, Collection> sets) { - for (var set : sets) { - if (set.contains(preservedRepresentative)) { - components.put(preservedRepresentative, set); - } else { - assignNewRepresentative(preservedRepresentative, set); - } - } - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java deleted file mode 100644 index 22159499..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.viatra.runtime.base.itc.alg.representative; - -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; - -import java.util.ArrayDeque; -import java.util.HashSet; -import java.util.Set; - -public class WeaklyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { - public WeaklyConnectedComponentAlgorithm(Graph graph) { - super(graph); - } - - @Override - protected void initializeComponents() { - for (var node : graph.getAllNodes()) { - if (representatives.containsKey(node)) { - continue; - } - var reachable = getReachableNodes(node); - initializeSet(reachable); - } - } - - @Override - public void edgeInserted(Object source, Object target) { - var sourceRoot = getRepresentative(source); - var targetRoot = getRepresentative(target); - merge(sourceRoot, targetRoot); - } - - @Override - public void edgeDeleted(Object source, Object target) { - var sourceRoot = getRepresentative(source); - var targetRoot = getRepresentative(target); - if (!sourceRoot.equals(targetRoot)) { - throw new IllegalArgumentException("Trying to remove edge not in graph"); - } - var targetReachable = getReachableNodes(target); - if (!targetReachable.contains(source)) { - split(sourceRoot, targetReachable); - } - } - - private void split(Object sourceRepresentative, Set targetReachable) { - var sourceComponent = getComponent(sourceRepresentative); - sourceComponent.removeAll(targetReachable); - if (targetReachable.contains(sourceRepresentative)) { - components.put(sourceRepresentative, targetReachable); - assignNewRepresentative(sourceRepresentative, sourceComponent); - } else { - assignNewRepresentative(sourceRepresentative, targetReachable); - } - } - - private Set getReachableNodes(Object source) { - var retSet = new HashSet<>(); - retSet.add(source); - var nodeQueue = new ArrayDeque<>(); - nodeQueue.addLast(source); - - while (!nodeQueue.isEmpty()) { - var node = nodeQueue.removeFirst(); - for (var neighbor : graph.getTargetNodes(node).distinctValues()) { - if (!retSet.contains(neighbor)) { - retSet.add(neighbor); - nodeQueue.addLast(neighbor); - } - } - for (var neighbor : graph.getSourceNodes(node).distinctValues()) { - if (!retSet.contains(neighbor)) { - retSet.add(neighbor); - nodeQueue.addLast(neighbor); - } - } - } - - return retSet; - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java deleted file mode 100644 index c9b3cafa..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.alg.util; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; - -/** - * @author Tamas Szabo - * - */ -public class CollectionHelper { - - private CollectionHelper() {/*Utility class constructor*/} - - /** - * Returns the intersection of two sets. It calls {@link Set#retainAll(java.util.Collection)} but returns a new set - * containing the elements of the intersection. - * - * @param set1 - * the first set (can be null, interpreted as empty) - * @param set2 - * the second set (can be null, interpreted as empty) - * @return the intersection of the sets - * @since 1.7 - */ - public static Set intersection(Set set1, Set set2) { - if (set1 == null || set2 == null) - return CollectionsFactory.createSet(); - - Set intersection = CollectionsFactory.createSet(set1); - intersection.retainAll(set2); - return intersection; - } - - - /** - * Returns the difference of two sets (S1\S2). It calls {@link Set#removeAll(java.util.Collection)} but returns a - * new set containing the elements of the difference. - * - * @param set1 - * the first set (can be null, interpreted as empty) - * @param set2 - * the second set (can be null, interpreted as empty) - * @return the difference of the sets - * @since 1.7 - */ - public static Set difference(Set set1, Set set2) { - if (set1 == null) - return CollectionsFactory.createSet(); - - Set difference = CollectionsFactory.createSet(set1); - if (set2 != null) difference.removeAll(set2); - return difference; - } - -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java deleted file mode 100644 index 0e21f323..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java +++ /dev/null @@ -1,160 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.graphimpl; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; -import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCCResult; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -/** - * This class contains utility methods to generate dot representations for {@link Graph} instances. - * - * @author Tamas Szabo - * @since 2.3 - */ -public class DotGenerator { - - private static final String[] colors = new String[] { "yellow", "blue", "red", "green", "gray", "cyan" }; - - private DotGenerator() { - - } - - /** - * Generates the dot representation for the given graph. - * - * @param graph - * the graph - * @param colorSCCs - * specifies if the strongly connected components with size greater than shall be colored - * @param nameFunction - * use this function to provide custom names to nodes, null if the default toString shall be used - * @param colorFunction - * use this function to provide custom color to nodes, null if the default white color shall be used - * @param edgeFunction - * use this function to provide custom edge labels, null if no edge label shall be printed - * @return the dot representation as a string - */ - public static String generateDot(final Graph graph, final boolean colorSCCs, - final Function nameFunction, final Function colorFunction, - final Function> edgeFunction) { - final Map colorMap = new HashMap(); - - if (colorSCCs) { - final SCCResult result = SCC.computeSCC(graph); - final Set> sccs = result.getSccs(); - - int i = 0; - for (final Set scc : sccs) { - if (scc.size() > 1) { - for (final V node : scc) { - final String color = colorMap.get(node); - if (color == null) { - colorMap.put(node, colors[i % colors.length]); - } else { - colorMap.put(node, colorMap.get(node) + ":" + colors[i % colors.length]); - } - } - i++; - } - } - - // if a node has no color yet, then make it white - for (final V node : graph.getAllNodes()) { - if (!colorMap.containsKey(node)) { - colorMap.put(node, "white"); - } - } - } else { - for (final V node : graph.getAllNodes()) { - colorMap.put(node, "white"); - } - } - - if (colorFunction != null) { - for (final V node : graph.getAllNodes()) { - colorMap.put(node, colorFunction.apply(node)); - } - } - - final StringBuilder builder = new StringBuilder(); - builder.append("digraph g {\n"); - - for (final V node : graph.getAllNodes()) { - final String nodePresentation = nameFunction == null ? node.toString() : nameFunction.apply(node); - builder.append("\"" + nodePresentation + "\""); - builder.append("[style=filled,fillcolor=" + colorMap.get(node) + "]"); - builder.append(";\n"); - } - - for (final V source : graph.getAllNodes()) { - final IMemoryView targets = graph.getTargetNodes(source); - if (!targets.isEmpty()) { - final String sourcePresentation = nameFunction == null ? source.toString() : nameFunction.apply(source); - for (final V target : targets.distinctValues()) { - String edgeLabel = null; - if (edgeFunction != null) { - final Function v1 = edgeFunction.apply(source); - if (v1 != null) { - edgeLabel = v1.apply(target); - } - } - - final String targetPresentation = nameFunction == null ? target.toString() - : nameFunction.apply(target); - - builder.append("\"" + sourcePresentation + "\" -> \"" + targetPresentation + "\""); - if (edgeLabel != null) { - builder.append("[label=\"" + edgeLabel + "\"]"); - } - builder.append(";\n"); - } - } - } - - builder.append("}"); - return builder.toString(); - } - - /** - * Generates the dot representation for the given graph. No special pretty printing customization will be applied. - * - * @param graph - * the graph - * @return the dot representation as a string - */ - public static String generateDot(final Graph graph) { - return generateDot(graph, false, null, null, null); - } - - /** - * Returns a simple name shortener function that can be used in the graphviz visualization to help with readability. - * WARNING: if you shorten the name of the {@link Node}s too much, the visualization may become incorrect because - * grahpviz will treat different nodes as the same if their shortened names are the same. - * - * @param maxLength - * the maximum length of the text that is kept from the toString of the objects in the graph - * @return the shrunk toString value - */ - public static Function getNameShortener(final int maxLength) { - return new Function() { - @Override - public String apply(final V obj) { - final String value = obj.toString(); - return value.substring(0, Math.min(value.length(), maxLength)); - } - }; - } - -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java deleted file mode 100644 index 70cbc77e..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java +++ /dev/null @@ -1,185 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.graphimpl; - -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; - -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -public class Graph implements IGraphDataSource, IBiDirectionalGraphDataSource { - - // source -> target -> count - private IMultiLookup outgoingEdges; - // target -> source -> count - private IMultiLookup incomingEdges; - - private Set nodes; - - private List> observers; - - public Graph() { - outgoingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); - incomingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); - nodes = CollectionsFactory.createSet(); - observers = CollectionsFactory.createObserverList(); - } - - public void insertEdge(V source, V target) { - outgoingEdges.addPair(source, target); - incomingEdges.addPair(target, source); - - for (IGraphObserver go : observers) { - go.edgeInserted(source, target); - } - } - - /** - * No-op if trying to delete edge that does not exist - * - * @since 2.0 - * @see #deleteEdgeIfExists(Object, Object) - */ - public void deleteEdgeIfExists(V source, V target) { - boolean containedEdge = outgoingEdges.lookupOrEmpty(source).containsNonZero(target); - if (containedEdge) { - deleteEdgeThatExists(source, target); - } - } - - /** - * @throws IllegalStateException - * if trying to delete edge that does not exist - * @since 2.0 - * @see #deleteEdgeIfExists(Object, Object) - */ - public void deleteEdgeThatExists(V source, V target) { - outgoingEdges.removePair(source, target); - incomingEdges.removePair(target, source); - for (IGraphObserver go : observers) { - go.edgeDeleted(source, target); - } - } - - /** - * @deprecated use explicitly {@link #deleteEdgeThatExists(Object, Object)} or - * {@link #deleteEdgeIfExists(Object, Object)} instead. To preserve backwards compatibility, this method - * delegates to the latter. - * - */ - @Deprecated - public void deleteEdge(V source, V target) { - deleteEdgeIfExists(source, target); - } - - /** - * Insert the given node into the graph. - */ - public void insertNode(V node) { - if (nodes.add(node)) { - for (IGraphObserver go : observers) { - go.nodeInserted(node); - } - } - } - - /** - * Deletes the given node AND all of the edges going in and out from the node. - */ - public void deleteNode(V node) { - if (nodes.remove(node)) { - IMemoryView incomingView = incomingEdges.lookup(node); - if (incomingView != null) { - Map incoming = CollectionsFactory.createMap(incomingView.asMap()); - - for (Entry entry : incoming.entrySet()) { - for (int i = 0; i < entry.getValue(); i++) { - deleteEdgeThatExists(entry.getKey(), node); - } - } - } - - IMemoryView outgoingView = outgoingEdges.lookup(node); - if (outgoingView != null) { - Map outgoing = CollectionsFactory.createMap(outgoingView.asMap()); - - for (Entry entry : outgoing.entrySet()) { - for (int i = 0; i < entry.getValue(); i++) { - deleteEdgeThatExists(node, entry.getKey()); - } - } - } - - for (IGraphObserver go : observers) { - go.nodeDeleted(node); - } - } - } - - @Override - public void attachObserver(IGraphObserver go) { - observers.add(go); - } - - @Override - public void attachAsFirstObserver(IGraphObserver observer) { - observers.add(0, observer); - } - - @Override - public void detachObserver(IGraphObserver go) { - observers.remove(go); - } - - @Override - public Set getAllNodes() { - return nodes; - } - - @Override - public IMemoryView getTargetNodes(V source) { - return outgoingEdges.lookupOrEmpty(source); - } - - @Override - public IMemoryView getSourceNodes(V target) { - return incomingEdges.lookupOrEmpty(target); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("nodes = "); - for (V n : getAllNodes()) { - sb.append(n.toString()); - sb.append(" "); - } - sb.append(" edges = "); - for (V source : outgoingEdges.distinctKeys()) { - IMemoryView targets = outgoingEdges.lookup(source); - for (V target : targets.distinctValues()) { - int count = targets.getCount(target); - for (int i = 0; i < count; i++) { - sb.append("(" + source + "," + target + ") "); - } - } - } - return sb.toString(); - } - -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java deleted file mode 100644 index 64659447..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiset; - -/** - * A bi-directional graph data source supports all operations that an {@link IGraphDataSource} does, but it - * also makes it possible to query the incoming edges of nodes, not only the outgoing edges. - * - * @author Tamas Szabo - * - * @param the type of the nodes in the graph - */ -public interface IBiDirectionalGraphDataSource extends IGraphDataSource { - - /** - * Returns the source nodes for the given target node. - * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source. - * - * The method must not return null. - * - * @param target the target node - * @return the multiset of source nodes - * @since 2.0 - */ - public IMemoryView getSourceNodes(V target); - -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java deleted file mode 100644 index becab0eb..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; - -/** - * This class can be used to wrap an {@link IGraphDataSource} into an {@link IBiDirectionalGraphDataSource}. This class - * provides support for the retrieval of source nodes for a given target which is not supported by standard - * {@link IGraphDataSource} implementations. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the nodes in the graph data source - */ -public class IBiDirectionalWrapper implements IBiDirectionalGraphDataSource, IGraphObserver { - - private IGraphDataSource wrappedDataSource; - // target -> source -> count - private IMultiLookup incomingEdges; - - public IBiDirectionalWrapper(IGraphDataSource gds) { - this.wrappedDataSource = gds; - - this.incomingEdges = CollectionsFactory.createMultiLookup( - Object.class, MemoryType.MULTISETS, Object.class); - - if (gds.getAllNodes() != null) { - for (V source : gds.getAllNodes()) { - IMemoryView targets = gds.getTargetNodes(source); - for (V target : targets.distinctValues()) { - int count = targets.getCount(target); - for (int i = 0; i < count; i++) { - edgeInserted(source, target); - } - } - } - } - - gds.attachAsFirstObserver(this); - } - - @Override - public void attachObserver(IGraphObserver observer) { - wrappedDataSource.attachObserver(observer); - } - - @Override - public void attachAsFirstObserver(IGraphObserver observer) { - wrappedDataSource.attachAsFirstObserver(observer); - } - - @Override - public void detachObserver(IGraphObserver observer) { - wrappedDataSource.detachObserver(observer); - } - - @Override - public Set getAllNodes() { - return wrappedDataSource.getAllNodes(); - } - - @Override - public IMemoryView getTargetNodes(V source) { - return wrappedDataSource.getTargetNodes(source); - } - - @Override - public IMemoryView getSourceNodes(V target) { - return incomingEdges.lookupOrEmpty(target); - } - - @Override - public void edgeInserted(V source, V target) { - incomingEdges.addPair(target, source); - } - - @Override - public void edgeDeleted(V source, V target) { - incomingEdges.removePair(target, source); - } - - @Override - public void nodeInserted(V n) { - - } - - @Override - public void nodeDeleted(V node) { - - } - - @Override - public String toString() { - return wrappedDataSource.toString(); - } -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java deleted file mode 100644 index 3fa65936..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiset; - -import java.util.Set; - -/** - * The interface prescribes the set of operations that a graph data source must support. - *

Note that the old version of the interface is broken at version 1.6; - * MultiSets are now presented as Maps instead of Lists. - * - * @author Tamas Szabo - * - * @param - * the type of the nodes in the graph - */ -public interface IGraphDataSource { - - /** - * Attaches a new graph observer to this graph data source. Observers will be notified in the order they have been registered. - * - * @param observer the graph observer - */ - public void attachObserver(IGraphObserver observer); - - /** - * Attaches a new graph observer to this graph data source as the first one. - * In the notification order this observer will be the first one as long as another call to this method happens. - * - * @param observer the graph observer - * @since 1.6 - */ - public void attachAsFirstObserver(IGraphObserver observer); - - /** - * Detaches an already registered graph observer from this graph data source. - * - * @param observer the graph observer - */ - public void detachObserver(IGraphObserver observer); - - /** - * Returns the complete set of nodes in the graph data source. - * - * @return the set of all nodes - */ - public Set getAllNodes(); - - /** - * Returns the target nodes for the given source node. - * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source. - * - * The method must not return null. - * - * @param source the source node - * @return the multiset of target nodes - * @since 2.0 - */ - public IMemoryView getTargetNodes(V source); -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java deleted file mode 100644 index 5cb2d9fa..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -/** - * Interface GraphObserver is used to observ the changes in a graph; edge and node insertion/deleteion. - * - * @author Tamas Szabo - * - */ -public interface IGraphObserver { - - /** - * Used to notify when an edge is inserted into the graph. - * - * @param source - * the source of the edge - * @param target - * the target of the edge - */ - public void edgeInserted(V source, V target); - - /** - * Used to notify when an edge is deleted from the graph. - * - * @param source - * the source of the edge - * @param target - * the target of the edge - */ - public void edgeDeleted(V source, V target); - - /** - * Used to notify when a node is inserted into the graph. - * - * @param n - * the node - */ - public void nodeInserted(V n); - - /** - * Used to notify when a node is deleted from the graph. - * - * @param n - * the node - */ - public void nodeDeleted(V n); -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java deleted file mode 100644 index 5924b723..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; - -/** - * This interface defines those methods that a transitive reachability data source should provide. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the node - */ -public interface ITcDataSource { - - /** - * Attach a transitive closure relation observer. - * - * @param to - * the observer object - */ - public void attachObserver(ITcObserver to); - - /** - * Detach a transitive closure relation observer. - * - * @param to - * the observer object - */ - public void detachObserver(ITcObserver to); - - /** - * Returns all nodes which are reachable from the source node. - * - * @param source - * the source node - * @return the set of target nodes - */ - public Set getAllReachableTargets(V source); - - /** - * Returns all nodes from which the target node is reachable. - * - * @param target - * the target node - * @return the set of source nodes - */ - public Set getAllReachableSources(V target); - - /** - * Returns true if the target node is reachable from the source node. - * - * @param source - * the source node - * @param target - * the target node - * @return true if target is reachable from source, false otherwise - */ - public boolean isReachable(V source, V target); - - /** - * The returned {@link IGraphPathFinder} can be used to retrieve paths between nodes using transitive reachability. - * - * @return a path finder for the graph. - */ - public IGraphPathFinder getPathFinder(); - - /** - * Call this method to properly dispose the data structures of a transitive closure algorithm. - */ - public void dispose(); -} diff --git a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java b/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java deleted file mode 100644 index fded53f1..00000000 --- a/subprojects/viatra-runtime-base-itc/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -/** - * Interface ITcObserver is used to observe the changes in a transitive closure relation; tuple insertion/deletion. - * - * @author Szabo Tamas - * - */ -public interface ITcObserver { - - /** - * Used to notify when a tuple is inserted into the transitive closure relation. - * - * @param source - * the source of the tuple - * @param target - * the target of the tuple - */ - public void tupleInserted(V source, V target); - - /** - * Used to notify when a tuple is deleted from the transitive closure relation. - * - * @param source - * the source of the tuple - * @param target - * the target of the tuple - */ - public void tupleDeleted(V source, V target); -} diff --git a/subprojects/viatra-runtime-matchers/about.html b/subprojects/viatra-runtime-matchers/about.html deleted file mode 100644 index d1d5593a..00000000 --- a/subprojects/viatra-runtime-matchers/about.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - -About - - - -

About This Content

- -

March 18, 2019

-

License

- -

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the -Eclipse Public License Version 2.0 ("EPL"). A copy of the EPL is available at http://www.eclipse.org/legal/epl-v20.html. -For purposes of the EPL, "Program" will mean the Content.

- -

If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may -apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise -indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at http://www.eclipse.org.

- - diff --git a/subprojects/viatra-runtime-matchers/build.gradle.kts b/subprojects/viatra-runtime-matchers/build.gradle.kts deleted file mode 100644 index 20a8c2c3..00000000 --- a/subprojects/viatra-runtime-matchers/build.gradle.kts +++ /dev/null @@ -1,15 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ - -plugins { - id("tools.refinery.gradle.java-library") -} - -dependencies { - implementation(libs.slf4j.log4j) - implementation(libs.eclipseCollections.api) - implementation(libs.eclipseCollections) -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java deleted file mode 100644 index 83f6f766..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers; - -/** - * A common base class for all exceptions thrown by various VIATRA Query Runtime APIs. - * - * @author Zoltan Ujhelyi - * @since 2.0 - */ -public abstract class ViatraQueryRuntimeException extends RuntimeException { - - private static final long serialVersionUID = -8505253058035069310L; - - public ViatraQueryRuntimeException() { - super(); - } - - public ViatraQueryRuntimeException(String message) { - super(message); - } - - public ViatraQueryRuntimeException(Throwable cause) { - super(cause); - } - - public ViatraQueryRuntimeException(String message, Throwable cause) { - super(message, cause); - } - - public ViatraQueryRuntimeException(String message, Throwable cause, boolean enableSuppression, - boolean writableStackTrace) { - super(message, cause, enableSuppression, writableStackTrace); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java deleted file mode 100644 index 2c09ede1..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java +++ /dev/null @@ -1,24 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -/** - * @since 2.0 - */ -class AverageAccumulator { - Domain value; - long count; - - public AverageAccumulator(Domain value, long count) { - super(); - this.value = value; - this.count = count; - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java deleted file mode 100644 index e8a26afd..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import java.util.OptionalDouble; -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; - -/** - * @author Zoltan Ujhelyi - * @since 2.0 - */ -public class DoubleAverageOperator implements IMultisetAggregationOperator, Double> { - - public static final DoubleAverageOperator INSTANCE = new DoubleAverageOperator(); - - private DoubleAverageOperator() { - // Singleton, do not call. - } - - @Override - public String getShortDescription() { - return "avg incrementally computes the average of java.lang.Integer values"; - } - - @Override - public String getName() { - return "avg"; - } - - @Override - public AverageAccumulator createNeutral() { - return new AverageAccumulator(0d, 0l); - } - - @Override - public boolean isNeutral(AverageAccumulator result) { - return result.count == 0l; - } - - @Override - public AverageAccumulator update(AverageAccumulator oldResult, Double updateValue, - boolean isInsertion) { - if (isInsertion) { - oldResult.value += updateValue; - oldResult.count++; - } else { - oldResult.value -= updateValue; - oldResult.count--; - } - return oldResult; - } - - @Override - public Double getAggregate(AverageAccumulator result) { - return (result.count == 0) - ? null - : result.value/result.count; - } - - @Override - public Double aggregateStream(Stream stream) { - final OptionalDouble averageOpt = stream.mapToDouble(Double::doubleValue).average(); - return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; - } - - /** - * @since 2.4 - */ - @Override - public AverageAccumulator clone(AverageAccumulator original) { - return new AverageAccumulator(original.value, original.count); - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java deleted file mode 100644 index 744b0cd1..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; - -/** - * Incrementally computes the sum of java.lang.Double values - * @author Gabor Bergmann - * @since 1.4 - */ -public class DoubleSumOperator extends AbstractMemorylessAggregationOperator { - public static final DoubleSumOperator INSTANCE = new DoubleSumOperator(); - - private DoubleSumOperator() { - // Singleton, do not call. - } - - @Override - public String getShortDescription() { - return "sum incrementally computes the sum of java.lang.Double values"; - } - @Override - public String getName() { - return "sum"; - } - - @Override - public Double createNeutral() { - return 0d; - } - - @Override - public boolean isNeutral(Double result) { - return createNeutral().equals(result); - } - - @Override - public Double update(Double oldResult, Double updateValue, boolean isInsertion) { - return isInsertion ? - oldResult + updateValue : - oldResult - updateValue; - } - - /** - * @since 2.0 - */ - @Override - public Double aggregateStream(Stream stream) { - return stream.mapToDouble(Double::doubleValue).sum(); - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java deleted file mode 100644 index ee4ceeb8..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java +++ /dev/null @@ -1,135 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import java.util.Comparator; -import java.util.SortedMap; -import java.util.TreeMap; -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; - -/** - * Incrementally computes the minimum or maximum of java.lang.Comparable values, using the default comparison - * - * @author Gabor Bergmann - * @since 1.4 - */ -public class ExtremumOperator> - implements IMultisetAggregationOperator, T> { - - public enum Extreme { - MIN, MAX; - - /** - * @since 2.0 - */ - public T pickFrom(SortedMap nonEmptyMultiSet) { - switch(this) { - case MIN: - return nonEmptyMultiSet.firstKey(); - case MAX: - return nonEmptyMultiSet.lastKey(); - default: - return null; - } - } - } - - private static final ExtremumOperator MIN_OP = new ExtremumOperator<>(Extreme.MIN); - private static final ExtremumOperator MAX_OP = new ExtremumOperator<>(Extreme.MAX); - - public static > ExtremumOperator getMin() { - return MIN_OP; - } - public static > ExtremumOperator getMax() { - return MAX_OP; - } - - Extreme extreme; - private ExtremumOperator(Extreme extreme) { - super(); - this.extreme = extreme; - } - - @Override - public String getShortDescription() { - String opName = getName(); - return String.format( - "%s incrementally computes the %simum of java.lang.Comparable values, using the default comparison", - opName, opName); - } - - @Override - public String getName() { - return extreme.name().toLowerCase(); - } - - /** - * @since 2.0 - */ - @Override - public SortedMap createNeutral() { - return new TreeMap<>(); - } - - /** - * @since 2.0 - */ - @Override - public boolean isNeutral(SortedMap result) { - return result.isEmpty(); - } - - /** - * @since 2.0 - */ - @Override - public SortedMap update(SortedMap oldResult, T updateValue, boolean isInsertion) { - oldResult.compute(updateValue, (value, c) -> { - int count = (c == null) ? 0 : c; - int result = (isInsertion) ? count+1 : count-1; - return (result == 0) ? null : result; - }); - return oldResult; - } - - /** - * @since 2.0 - */ - @Override - public T getAggregate(SortedMap result) { - return result.isEmpty() ? null : - extreme.pickFrom(result); - } - - /** - * @since 2.0 - */ - @Override - public T aggregateStream(Stream stream) { - switch (extreme) { - case MIN: - return stream.min(Comparator.naturalOrder()).orElse(null); - case MAX: - return stream.max(Comparator.naturalOrder()).orElse(null); - default: - return null; - } - } - - /** - * @since 2.4 - */ - @Override - public SortedMap clone(SortedMap original) { - return new TreeMap(original); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java deleted file mode 100644 index bf422476..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import java.util.OptionalDouble; -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; - -/** - * @author Zoltan Ujhelyi - * @since 2.0 - */ -public class IntegerAverageOperator implements IMultisetAggregationOperator, Double> { - - public static final IntegerAverageOperator INSTANCE = new IntegerAverageOperator(); - - private IntegerAverageOperator() { - // Singleton, do not call. - } - - @Override - public String getShortDescription() { - return "avg incrementally computes the average of java.lang.Integer values"; - } - - @Override - public String getName() { - return "avg"; - } - - @Override - public AverageAccumulator createNeutral() { - return new AverageAccumulator(0, 0l); - } - - @Override - public boolean isNeutral(AverageAccumulator result) { - return result.count == 0l; - } - - @Override - public AverageAccumulator update(AverageAccumulator oldResult, Integer updateValue, - boolean isInsertion) { - if (isInsertion) { - oldResult.value += updateValue; - oldResult.count++; - } else { - oldResult.value -= updateValue; - oldResult.count--; - } - return oldResult; - } - - @Override - public Double getAggregate(AverageAccumulator result) { - return (result.count == 0) - ? null - : ((double)result.value)/result.count; - } - - @Override - public Double aggregateStream(Stream stream) { - final OptionalDouble averageOpt = stream.mapToInt(Integer::intValue).average(); - return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; - } - - /** - * @since 2.4 - */ - @Override - public AverageAccumulator clone(AverageAccumulator original) { - return new AverageAccumulator(original.value, original.count); - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java deleted file mode 100644 index 18584256..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; - -/** - * Incrementally computes the sum of java.lang.Integer values - * @author Gabor Bergmann - * @since 1.4 - */ -public class IntegerSumOperator extends AbstractMemorylessAggregationOperator { - public static final IntegerSumOperator INSTANCE = new IntegerSumOperator(); - - private IntegerSumOperator() { - // Singleton, do not call. - } - - @Override - public String getShortDescription() { - return "sum incrementally computes the sum of java.lang.Integer values"; - } - @Override - public String getName() { - return "sum"; - } - - @Override - public Integer createNeutral() { - return 0; - } - - @Override - public boolean isNeutral(Integer result) { - return createNeutral().equals(result); - } - - @Override - public Integer update(Integer oldResult, Integer updateValue, boolean isInsertion) { - return isInsertion ? - oldResult + updateValue : - oldResult - updateValue; - } - - /** - * @since 2.0 - */ - @Override - public Integer aggregateStream(Stream stream) { - return stream.mapToInt(Integer::intValue).sum(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java deleted file mode 100644 index d56c9507..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import java.util.OptionalDouble; -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; - -/** - * @author Zoltan Ujhelyi - * @since 2.0 - */ -public class LongAverageOperator implements IMultisetAggregationOperator, Double> { - - public static final LongAverageOperator INSTANCE = new LongAverageOperator(); - - private LongAverageOperator() { - // Singleton, do not call. - } - - @Override - public String getShortDescription() { - return "avg incrementally computes the average of java.lang.Integer values"; - } - - @Override - public String getName() { - return "avg"; - } - - @Override - public AverageAccumulator createNeutral() { - return new AverageAccumulator(0l, 0l); - } - - @Override - public boolean isNeutral(AverageAccumulator result) { - return result.count == 0l; - } - - @Override - public AverageAccumulator update(AverageAccumulator oldResult, Long updateValue, - boolean isInsertion) { - if (isInsertion) { - oldResult.value += updateValue; - oldResult.count++; - } else { - oldResult.value -= updateValue; - oldResult.count--; - } - return oldResult; - } - - @Override - public Double getAggregate(AverageAccumulator result) { - return (result.count == 0) - ? null - : ((double)result.value)/result.count; - } - - @Override - public Double aggregateStream(Stream stream) { - final OptionalDouble averageOpt = stream.mapToLong(Long::longValue).average(); - return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; - } - - /** - * @since 2.4 - */ - @Override - public AverageAccumulator clone(AverageAccumulator original) { - return new AverageAccumulator(original.value, original.count); - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java deleted file mode 100644 index 29ded090..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; - -/** - * Incrementally computes the sum of java.lang.Long values - * @author Gabor Bergmann - * @since 1.4 - */ -public class LongSumOperator extends AbstractMemorylessAggregationOperator { - public static final LongSumOperator INSTANCE = new LongSumOperator(); - - private LongSumOperator() { - // Singleton, do not call. - } - - @Override - public String getShortDescription() { - return "sum incrementally computes the sum of java.lang.Long values"; - } - @Override - public String getName() { - return "sum"; - } - - @Override - public Long createNeutral() { - return 0L; - } - - @Override - public boolean isNeutral(Long result) { - return createNeutral().equals(result); - } - - @Override - public Long update(Long oldResult, Long updateValue, boolean isInsertion) { - return isInsertion ? - oldResult + updateValue : - oldResult - updateValue; - } - - /** - * @since 2.0 - */ - @Override - public Long aggregateStream(Stream stream) { - return stream.mapToLong(Long::longValue).sum(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java deleted file mode 100644 index c25678aa..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; - -/** - * This aggregator calculates the average of the values of a selected aggregate parameter of a called pattern. The aggregate - * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The - * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the - * summation. - * - * @since 2.0 - * - */ -@AggregatorType( - parameterTypes = {Integer.class, Double.class, Long.class}, - returnTypes = {Double.class, Double.class, Double.class}) -public final class avg implements IAggregatorFactory { - - @Override - public BoundAggregator getAggregatorLogic(Class domainClass) { - if (Integer.class.equals(domainClass)) - return new BoundAggregator(IntegerAverageOperator.INSTANCE, Integer.class, Double.class); - if (Double.class.equals(domainClass)) - return new BoundAggregator(DoubleAverageOperator.INSTANCE, Double.class, Double.class); - if (Long.class.equals(domainClass)) - return new BoundAggregator(LongAverageOperator.INSTANCE, Long.class, Double.class); - else throw new IllegalArgumentException(); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java deleted file mode 100644 index 8310a0ce..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; - -/** - * An aggregator to count the number of matches a pattern has. The return of the aggregator is an non-negative integer - * number. - * - * @since 1.4 - * - */ -@AggregatorType(parameterTypes = {Void.class}, returnTypes = {Integer.class}) -public final class count implements IAggregatorFactory { - - @Override - public BoundAggregator getAggregatorLogic(Class domainClass) { - if (Void.class.equals(domainClass)) - return new BoundAggregator(null, Void.class, Integer.class); - else throw new IllegalArgumentException(); - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java deleted file mode 100644 index e0236223..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Calendar; -import java.util.Date; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; - -/** - * This aggregator calculates the maximum value of a selected aggregate parameter of a called pattern. The aggregate - * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The - * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the - * minimum calculation. - * - * @since 1.4 - * @author Gabor Bergmann - */ -@AggregatorType( - // TODO T extends Comparable? - parameterTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, - Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}, - returnTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, - Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}) -public final class max implements IAggregatorFactory { - - @Override - public BoundAggregator getAggregatorLogic(Class domainClass) { - if (Comparable.class.isAssignableFrom(domainClass)) - return new BoundAggregator(ExtremumOperator.getMax(), domainClass, domainClass); - else throw new IllegalArgumentException(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java deleted file mode 100644 index 6408c57b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.Calendar; -import java.util.Date; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; - -/** - * This aggregator calculates the minimum value of a selected aggregate parameter of a called pattern. The aggregate - * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The - * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the - * minimum calculation. - * - * @since 1.4 - * - */ -@AggregatorType( - // TODO T extends Comparable? - parameterTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, - Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}, - returnTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, - Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}) -public final class min implements IAggregatorFactory { - - @Override - public BoundAggregator getAggregatorLogic(Class domainClass) { - if (Comparable.class.isAssignableFrom(domainClass)) - return new BoundAggregator(ExtremumOperator.getMin(), domainClass, domainClass); - else throw new IllegalArgumentException(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java deleted file mode 100644 index 69ff2e75..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.aggregators; - -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; - -/** - * This aggregator calculates the sum of the values of a selected aggregate parameter of a called pattern. The aggregate - * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The - * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the - * summation. - * - * @since 1.4 - * - */ -@AggregatorType( - parameterTypes = {Integer.class, Double.class, Long.class}, - returnTypes = {Integer.class, Double.class, Long.class}) -public final class sum implements IAggregatorFactory { - - @Override - public BoundAggregator getAggregatorLogic(Class domainClass) { - if (Integer.class.equals(domainClass)) - return new BoundAggregator(IntegerSumOperator.INSTANCE, Integer.class, Integer.class); - if (Double.class.equals(domainClass)) - return new BoundAggregator(DoubleSumOperator.INSTANCE, Double.class, Double.class); - if (Long.class.equals(domainClass)) - return new BoundAggregator(LongSumOperator.INSTANCE, Long.class, Long.class); - else throw new IllegalArgumentException(); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java deleted file mode 100644 index 1917e909..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java +++ /dev/null @@ -1,83 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.algorithms; - -import java.util.Comparator; -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * @author Gabor Bergmann - * @since 2.1 - * - */ -public class OrderedIterableMerge { - - private OrderedIterableMerge() { - // Hidden utility class constructor - } - - /** - * Lazily merges two iterables, each ordered according to a given comparator. - * Retains order in the result, and also eliminates any duplicates that appear in both arguments. - */ - public static Iterable mergeUniques(Iterable first, Iterable second, Comparator comparator) { - return () -> new Iterator() { - Iterator firstIterator = first.iterator(); - Iterator secondIterator = second.iterator(); - T firstItem; - T secondItem; - - { - fetchFirst(); - fetchSecond(); - } - - private T fetchFirst() { - T previous = firstItem; - if (firstIterator.hasNext()) - firstItem = firstIterator.next(); - else - firstItem = null; - return previous; - } - private T fetchSecond() { - T previous = secondItem; - if (secondIterator.hasNext()) - secondItem = secondIterator.next(); - else - secondItem = null; - return previous; - } - - @Override - public boolean hasNext() { - return firstItem != null || secondItem != null; - } - @Override - public T next() { - if (!hasNext()) throw new NoSuchElementException(); - if (firstItem != null && secondItem != null) { - if (secondItem == firstItem) { // duplicates - fetchFirst(); - return fetchSecond(); - } else if (comparator.compare(firstItem, secondItem) < 0) { - return fetchFirst(); - } else { - return fetchSecond(); - } - } else if (firstItem != null) { - return fetchFirst(); - } else { // secondItem must be non-null - return fetchSecond(); - } - } - }; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java deleted file mode 100644 index c69f08e5..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java +++ /dev/null @@ -1,214 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.algorithms; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; - -/** - * Union-find data structure implementation. Note that the implementation relies on the correct implementation of the - * equals method of the type parameter's class. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the element's stored in the union-find data structure - */ -public class UnionFind { - - private final Map> nodeMap; - final Map> setMap; - - /** - * Instantiate a new union-find data structure. - */ - public UnionFind() { - nodeMap = CollectionsFactory.createMap(); - setMap = CollectionsFactory.createMap(); - } - - /** - * Instantiate a new union-find data structure with the given elements as separate sets. - */ - public UnionFind(Iterable elements) { - this(); - for (V element : elements) { - makeSet(element); - } - } - - /** - * Creates a new union set from a collection of elements. - * - * @param nodes - * the collection of elements - * @return the root element - */ - public V makeSet(Collection nodes) { - if (!nodes.isEmpty()) { - Iterator iterator = nodes.iterator(); - V root = makeSet(iterator.next()); - while (iterator.hasNext()) { - root = union(root, iterator.next()); - } - return root; - } else { - return null; - } - } - - /** - * This method creates a single set containing the given node. - * - * @param node - * the root node of the set - * @return the root element - */ - public V makeSet(V node) { - if (!nodeMap.containsKey(node)) { - UnionFindNodeProperty prop = new UnionFindNodeProperty(0, node); - nodeMap.put(node, prop); - Set set = new HashSet(); - set.add(node); - setMap.put(node, set); - } - return node; - } - - /** - * Find method with path compression. - * - * @param node - * the node to find - * @return the root node of the set in which the given node can be found - */ - public V find(V node) { - UnionFindNodeProperty prop = nodeMap.get(node); - - if (prop != null) { - if (prop.parent.equals(node)) { - return node; - } else { - prop.parent = find(prop.parent); - return prop.parent; - } - } - return null; - } - - /** - * Union by rank implementation of the two sets which contain x and y; x and/or y can be a single element from the - * universe. - * - * @param x - * set or single element of the universe - * @param y - * set or single element of the universe - * @return the new root of the two sets - */ - public V union(V x, V y) { - V xRoot = find(x); - V yRoot = find(y); - - if ((xRoot == null) || (yRoot == null)) { - return union( (xRoot == null) ? makeSet(x) : xRoot, (yRoot == null) ? makeSet(y) : yRoot); - } - else if (!xRoot.equals(yRoot)) { - UnionFindNodeProperty xRootProp = nodeMap.get(xRoot); - UnionFindNodeProperty yRootProp = nodeMap.get(yRoot); - - if (xRootProp.rank < yRootProp.rank) { - xRootProp.parent = yRoot; - setMap.get(yRoot).addAll(setMap.get(xRoot)); - setMap.remove(xRoot); - return yRoot; - } else {// (xRootProp.rank >= yRootProp.rank) - yRootProp.parent = xRoot; - yRootProp.rank = (xRootProp.rank == yRootProp.rank) ? yRootProp.rank + 1 : yRootProp.rank; - setMap.get(xRoot).addAll(setMap.get(yRoot)); - setMap.remove(yRoot); - return xRoot; - } - } else { - return xRoot; - } - } - - /** - * Places the given elements in to the same partition. - */ - public void unite(Set elements) { - if (elements.size() > 1) { - V current = null; - for (V element : elements) { - if (current != null) { - if (getPartition(element) != null) { - union(current, element); - } - } else { - if (getPartition(element) != null) { - current = element; - } - } - } - } - } - - /** - * Delete the set whose root is the given node. - * - * @param root - * the root node - */ - public void deleteSet(V root) { - // if (setMap.containsKey(root)) - for (V n : setMap.get(root)) { - nodeMap.remove(n); - } - setMap.remove(root); - } - - /** - * Returns if all given elements are in the same partition. - */ - public boolean isSameUnion(Set elements) { - for (Set partition : setMap.values()) { - if (partition.containsAll(elements)) { - return true; - } - } - return false; - } - - - /** - * Returns the partition in which the given element can be found, or null otherwise. - */ - public Set getPartition(V element) { - V root = find(element); - return setMap.get(root); - } - - /** - * Returns all partitions. - */ - public Collection> getPartitions() { - return setMap.values(); - } - - public Set getPartitionHeads() { - return setMap.keySet(); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java deleted file mode 100644 index 82852f9c..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java +++ /dev/null @@ -1,32 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.algorithms; - -public class UnionFindNodeProperty { - - public int rank; - public V parent; - - public UnionFindNodeProperty() { - this.rank = 0; - this.parent = null; - } - - public UnionFindNodeProperty(int rank, V parent) { - super(); - this.rank = rank; - this.parent = parent; - } - - @Override - public String toString() { - return "[rank:" + rank + ", parent:" + parent.toString() + "]"; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java deleted file mode 100644 index 317293bf..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IRewriterTraceCollector; -import tools.refinery.viatra.runtime.matchers.psystem.rewriters.NopTraceCollector; - -/** - * Query evaluation hints applicable to any engine - * @since 1.6 - * - */ -public final class CommonQueryHintOptions { - - private CommonQueryHintOptions() { - // Hiding constructor for utility class - } - - /** - * This hint instructs the query backends to record trace information into the given trace collector - */ - public static final QueryHintOption normalizationTraceCollector = - hintOption("normalizationTraceCollector", NopTraceCollector.INSTANCE); - - // internal helper for conciseness - private static QueryHintOption hintOption(String hintKeyLocalName, T defaultValue) { - return new QueryHintOption<>(CommonQueryHintOptions.class, hintKeyLocalName, defaultValue); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java deleted file mode 100644 index 40853f46..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; -import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; - -/** - * Function object that specifies how hints (including backend preferences) shall propagate through pattern calls. - * - *

A few useful default implementations are included as static fields. - * - *

As of 2.1, only suppported by the local search backend, and only if the pattern call is not flattened. - * - * @author Gabor Bergmann - * @since 2.1 - */ -@FunctionalInterface -public interface ICallDelegationStrategy { - - /** - * Specifies how hints (including backend preferences) shall propagate through pattern calls. - * - * @param call a {@link PConstraint} in a query that calls another query. - * @param callerHint a hint under which the calling pattern is evaluated, - * @param callerBackend the actual backend evaluating the calling pattern. - * @param calleeHintProvider the provider of hints for the called pattern. - * @return the hints, including backend selection, - * that the backend responsible for the caller pattern must specify when - * requesting the {@link IQueryResultProvider} for the called pattern via {@link IQueryResultProviderAccess}. - */ - public QueryEvaluationHint transformHints(IQueryReference call, - QueryEvaluationHint callerHint, - IQueryBackend callerBackend, - IQueryBackendHintProvider calleeHintProvider); - - - /** - * Options known for callee are used to override caller options, except the backend selection. - * Always use the same backend for the callee and the caller, regardless what is specified for the callee pattern. - * @author Gabor Bergmann - */ - public static final ICallDelegationStrategy FULL_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { - QueryEvaluationHint calleeHint = - calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); - QueryEvaluationHint result = - callerHint == null ? calleeHint : callerHint.overrideBy(calleeHint); - - QueryEvaluationHint backendAdhesion = new QueryEvaluationHint( - null /* settings-ignorant */, callerBackend.getFactory()); - result = result.overrideBy(backendAdhesion); - return result; - }; - /** - * Options known for callee are used to override caller options, including the backend selection. - * If callee does not specify a backend requirement, the backend of the caller is kept. - * @author Gabor Bergmann - */ - public static final ICallDelegationStrategy PARTIAL_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { - QueryEvaluationHint backendAdhesion = new QueryEvaluationHint( - null /* settings-ignorant */, callerBackend.getFactory()); - - QueryEvaluationHint result = - callerHint == null ? backendAdhesion : callerHint.overrideBy(backendAdhesion); - - QueryEvaluationHint calleeHint = calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); - result = result.overrideBy(calleeHint); - - return result; - }; - /** - * Options known for callee are used to override caller options, including the backend selection. - * Always use the backend specified for the callee (or the default if none), regardless of the backend of the caller. - * @author Gabor Bergmann - */ - public static final ICallDelegationStrategy NO_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { - QueryEvaluationHint calleeHint = calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); - return callerHint == null ? calleeHint : callerHint.overrideBy(calleeHint); - }; - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java deleted file mode 100644 index 104b68a8..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -/** - * Implementations of this interface can be used to decide whether a matcher created by an arbitrary backend can - * potentially be used as a substitute for another matcher. - * - * @author Grill Balázs - * @since 1.4 - * - */ -public interface IMatcherCapability { - - /** - * Returns true if matchers of this capability can be used as a substitute for a matcher implementing the given capability - */ - public boolean canBeSubstitute(IMatcherCapability capability); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java deleted file mode 100644 index c85f10a4..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * Internal interface for a VIATRA query specification. Each query is associated with a pattern. Methods instantiate a matcher - * of the pattern with various parameters. - * - * @author Bergmann Gábor - * @since 0.9 - * @noextend This interface is not intended to be extended by users of the VIATRA framework, and should only be used by the query engine - */ -public interface IQueryBackend { - - /** - * @return true iff this backend is incremental, i.e. it caches the results of queries for quick retrieval, - * and can provide update notifications on result set changes. - */ - public boolean isCaching(); - - /** - * Returns a result provider for a given query. Repeated calls may return the same instance. - * @throws ViatraQueryRuntimeException - */ - public IQueryResultProvider getResultProvider(PQuery query); - - /** - * Returns a result provider for a given query. Repeated calls may return the same instance. - * @param optional hints that may override engine and query defaults (as provided by {@link IQueryBackendHintProvider}). Can be null. - * @throws ViatraQueryRuntimeException - * @since 1.4 - */ - public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints); - - /** - * Returns an existing result provider for a given query, if it was previously constructed, returns null otherwise. - * Will not construct and initialize new result providers. - */ - public IQueryResultProvider peekExistingResultProvider(PQuery query); - - /** - * Propagates all pending updates in this query backend. The implementation of this method is optional, and it - * can be ignored entirely if the backend does not delay updates. - * @since 1.6 - */ - public void flushUpdates(); - - /** - * Disposes the query backend. - */ - public abstract void dispose(); - - /** - * @return the factory that created this backend, if this functionality is supported - * @since 2.1 - */ - public IQueryBackendFactory getFactory(); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java deleted file mode 100644 index e264ab3f..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * A Query Backend Factory identifies a query evaluator implementation, and can create an evaluator instance (an {@link IQueryBackend}) tied to a specific VIATRA Query engine upon request. - * - *

The factory is used as a lookup key for the backend instance, - * therefore implementors should either be singletons, or implement equals() / hashCode() accordingly. - * - * @author Bergmann Gabor - * - */ -public interface IQueryBackendFactory { - - /** - * Creates a new {@link IQueryBackend} instance tied to the given context elements. - * - * @return an instance of the class returned by {@link #getBackendClass()} that operates in the given context. - * @since 1.5 - */ - public IQueryBackend - create(IQueryBackendContext context); - - - /** - * The backend instances created by this factory are guaranteed to conform to the returned class. - */ - public Class getBackendClass(); - - /** - * Calculate the required capabilities, which are needed to execute the given pattern - * - * @since 1.4 - */ - public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint); - - /** - * Returns whether the current backend is caching - * @since 2.0 - */ - public boolean isCaching(); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java deleted file mode 100644 index 8787814e..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -/** - * A provider interface for {@link IQueryBackendFactory} instances. - * @since 2.0 - */ -public interface IQueryBackendFactoryProvider { - - /** - * Returns a query backend factory instance. The method should return the same instance in case of repeated calls. - */ - IQueryBackendFactory getFactory(); - - /** - * Returns whether the given query backend should be considered as system default. If multiple backends are - * registered as system default, it is undefined which one will be chosen. - */ - default boolean isSystemDefaultEngine() { - return false; - } - - /** - * Returns whether the given query backend should be considered as system default search backend. If multiple - * backends are registered as system default, it is undefined which one will be chosen. - */ - default boolean isSystemDefaultSearchBackend() { - return false; - } - - - /** - * Returns whether the given query backend should be considered as system default caching backend. If multiple - * backends are registered as system default, it is undefined which one will be chosen. - */ - default boolean isSystemDefaultCachingBackend() { - return false; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java deleted file mode 100644 index 9bb76349..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java +++ /dev/null @@ -1,32 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * Provides query evaluation hints consisting of the Engine default hints and - * the hints provided by the pattern itself. - * - * @author Bergmann Gabor - * @since 0.9 - * @noimplement This interface is not intended to be implemented by clients, except in the tools.refinery.viatra.runtime plugin. - */ -public interface IQueryBackendHintProvider { - - /** - * Suggests query evaluation hints regarding a query. The returned hints reflects the default hints of the - * query engine merged with the hints provided by the pattern itself. These can be overridden via specific - * advanced API of the engine. - * - * @since 1.4 - */ - QueryEvaluationHint getQueryEvaluationHint(PQuery query); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java deleted file mode 100644 index cd7d050f..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java +++ /dev/null @@ -1,202 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -import java.util.Optional; -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.Accuracy; - -/** - * An internal interface of the query backend that provides results of a given query. - * @author Bergmann Gabor - * @noimplement This interface is not intended to be implemented by clients. - */ -public interface IQueryResultProvider { - - /** - * Decides whether there are any matches of the pattern that conform to the given fixed values of some parameters. - * - * @param parameters - * array where each non-null element binds the corresponding pattern parameter to a fixed value. - * @pre size of input array must be equal to the number of parameters. - * @since 2.0 - */ - public boolean hasMatch(Object[] parameters); - - /** - * Decides whether there are any matches of the pattern that conform to the given fixed values of some parameters. - * - * @param parameterSeedMask - * a mask that extracts those parameters of the query (from the entire parameter list) that should be - * bound to a fixed value - * @param parameters - * the tuple of fixed values restricting the match set to be considered, in the same order as given in - * parameterSeedMask, so that for each considered match tuple, - * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold - * @since 2.0 - */ - public boolean hasMatch(TupleMask parameterSeedMask, ITuple projectedParameterSeed); - - /** - * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters. - * - * @param parameters - * array where each non-null element binds the corresponding pattern parameter to a fixed value. - * @pre size of input array must be equal to the number of parameters. - * @return the number of pattern matches found. - */ - public int countMatches(Object[] parameters); - - /** - * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters. - * - * @param parameterSeedMask - * a mask that extracts those parameters of the query (from the entire parameter list) that should be - * bound to a fixed value - * @param parameters - * the tuple of fixed values restricting the match set to be considered, in the same order as given in - * parameterSeedMask, so that for each considered match tuple, - * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold - * @return the number of pattern matches found. - * @since 1.7 - */ - public int countMatches(TupleMask parameterSeedMask, ITuple projectedParameterSeed); - - /** - * Gives an estimate of the number of different groups the matches are projected into by the given mask - * (e.g. for an identity mask, this means the full match set size). The estimate must meet the required accuracy. - * - *

If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. - * In other words, query backends may deny an answer, or do their best to give an estimate without actually determining the match set of the query. - * However, caching backends are expected to simply return the indexed (projection) size, initialized on-demand if necessary. - * - *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. - * - * @return if available, an estimate of the cardinality of the projection of the match set, with the desired accuracy. - * - * @since 2.1 - */ - public Optional estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy); - - /** - * Gives an estimate of the average size of different groups the matches are projected into by the given mask - * (e.g. for an identity mask, this means 1, while for an empty mask, the result is match set size). - * The estimate must meet the required accuracy. - * - *

If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. - * In other words, query backends may deny an answer, or do their best to give an estimate without actually determining the match set of the query. - * However, caching backends are expected to simply return the exact value from the index, initialized on-demand if necessary. - * - *

For an empty match set, zero is acceptable as an exact answer. - * - *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. - * - * @return if available, an estimate of the average size of each projection group of the match set, with the desired accuracy. - * - * @since 2.1 - */ - public default Optional estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy) { - return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, this::estimateCardinality); - } - - /** - * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters. - * Neither determinism nor randomness of selection is guaranteed. - * - * @param parameters - * array where each non-null element binds the corresponding pattern parameter to a fixed value. - * @pre size of input array must be equal to the number of parameters. - * @return a match represented in the internal {@link Tuple} representation. - * @since 2.0 - */ - public Optional getOneArbitraryMatch(Object[] parameters); - - /** - * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters. - * Neither determinism nor randomness of selection is guaranteed. - * - * @param parameterSeedMask - * a mask that extracts those parameters of the query (from the entire parameter list) that should be - * bound to a fixed value - * @param parameters - * the tuple of fixed values restricting the match set to be considered, in the same order as given in - * parameterSeedMask, so that for each considered match tuple, - * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold - * @return a match represented in the internal {@link Tuple} representation. - * @since 2.0 - */ - public Optional getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters); - - /** - * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters. - * - * @param parameters - * array where each non-null element binds the corresponding pattern parameter to a fixed value. - * @pre size of input array must be equal to the number of parameters. - * @return matches represented in the internal {@link Tuple} representation. - * @since 2.0 - */ - public Stream getAllMatches(Object[] parameters); - - /** - * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters. - * - * @param parameterSeedMask - * a mask that extracts those parameters of the query (from the entire parameter list) that should be - * bound to a fixed value - * @param parameters - * the tuple of fixed values restricting the match set to be considered, in the same order as given in - * parameterSeedMask, so that for each considered match tuple, - * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold - * @return matches represented in the internal {@link Tuple} representation. - * @since 2.0 - */ - public Stream getAllMatches(TupleMask parameterSeedMask, ITuple parameters); - - /** - * The underlying query evaluator backend. - */ - public IQueryBackend getQueryBackend(); - - /** - * Internal method that registers low-level callbacks for match appearance and disappearance. - * - *

- * Caution: This is a low-level callback that is invoked when the pattern matcher is not necessarily in a - * consistent state yet. Importantly, no model modification permitted during the callback. - * - *

- * The callback can be unregistered via invoking {@link #removeUpdateListener(Object)} with the same tag. - * - * @param listener - * the listener that will be notified of each new match that appears or disappears, starting from now. - * @param listenerTag - * a tag by which to identify the listener for later removal by {@link #removeUpdateListener(Object)}. - * @param fireNow - * if true, the insertion update allback will be immediately invoked on all current matches as a one-time effect. - * - * @throws UnsupportedOperationException if this is a non-incremental backend - * (i.e. {@link IQueryBackend#isCaching()} on {@link #getQueryBackend()} returns false) - */ - public void addUpdateListener(final IUpdateable listener, final Object listenerTag, boolean fireNow); - - /** - * Removes an existing listener previously registered with the given tag. - * - * @throws UnsupportedOperationException if this is a non-incremental backend - * (i.e. {@link IQueryBackend#isCaching()} on {@link #getQueryBackend()} returns false) - */ - public void removeUpdateListener(final Object listenerTag); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java deleted file mode 100644 index baf7144a..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * Internal interface for the query backend to singal an update to a query result. - * @author Bergmann Gabor - * @since 0.9 - * - */ -public interface IUpdateable { - - /** - * This callback method must be free of exceptions, even {@link RuntimeException}s (though not {@link Error}s). - * @param updateElement the tuple that is changed - * @param isInsertion true if the tuple appeared in the result set, false if disappeared from the result set - */ - public void update(Tuple updateElement, boolean isInsertion); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java deleted file mode 100644 index eab92128..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java +++ /dev/null @@ -1,241 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -import java.util.AbstractMap; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; -import java.util.stream.Collectors; - -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * Provides VIATRA Query with additional hints on how a query should be evaluated. The same hint can be provided to multiple queries. - * - *

This class is immutable. Overriding options will create a new instance. - * - *

- * Here be dragons: for advanced users only. - * - * @author Bergmann Gabor - * - */ -public class QueryEvaluationHint { - - /** - * @since 2.0 - * - */ - public enum BackendRequirement { - /** - * The current hint does not specify any backend requirement - */ - UNSPECIFIED, - /** - * The current hint specifies that the default search backend of the engine should be used - */ - DEFAULT_SEARCH, - /** - * The current hint specifies that the default caching backend of the engine should be used - */ - DEFAULT_CACHING, - /** - * The current hint specifies that a specific backend is to be used - */ - SPECIFIC - } - - final IQueryBackendFactory queryBackendFactory; - final Map, Object> backendHintSettings; - final BackendRequirement requirement; - - /** - * Specifies the suggested query backend requirements, and value settings for additional backend-specific options. - * - *

- * The backend requirement type must not be {@link BackendRequirement#SPECIFIC} - for that case, use the constructor - * {@link #QueryEvaluationHint(Map, IQueryBackendFactory)}. - * - * @param backendHintSettings - * if non-null, each entry in the map overrides backend-specific options regarding query evaluation - * (null-valued map entries permitted to erase hints); passing null means default options associated with - * the query - * @param backendRequirementType - * defines the kind of backend requirement - * @since 2.0 - */ - public QueryEvaluationHint(Map, Object> backendHintSettings, BackendRequirement backendRequirementType) { - super(); - Preconditions.checkArgument(backendRequirementType != null, "Specific requirement needs to be set"); - Preconditions.checkArgument(backendRequirementType != BackendRequirement.SPECIFIC, "Specific backend requirement needs providing a corresponding backend type"); - this.queryBackendFactory = null; - this.requirement = backendRequirementType; - this.backendHintSettings = (backendHintSettings == null) - ? Collections., Object> emptyMap() - : new HashMap<>(backendHintSettings); - } - - /** - * Specifies the suggested query backend, and value settings for additional backend-specific options. The first - * parameter can be null; if the second parameter is null, it is expected that the other constructor is called - * instead with a {@link BackendRequirement#UNSPECIFIED} parameter. - * - * @param backendHintSettings - * if non-null, each entry in the map overrides backend-specific options regarding query evaluation - * (null-valued map entries permitted to erase hints); passing null means default options associated with - * the query - * @param queryBackendFactory - * overrides the query evaluator algorithm; passing null retains the default algorithm associated with - * the query - * @since 1.5 - */ - public QueryEvaluationHint( - Map, Object> backendHintSettings, - IQueryBackendFactory queryBackendFactory) { - super(); - this.queryBackendFactory = queryBackendFactory; - this.requirement = (queryBackendFactory == null) ? BackendRequirement.UNSPECIFIED : BackendRequirement.SPECIFIC; - this.backendHintSettings = (backendHintSettings == null) - ? Collections., Object> emptyMap() - : new HashMap<>(backendHintSettings); - } - - /** - * Returns the backend requirement described by this hint. If a specific backend is required, that can be queried by {@link #getQueryBackendFactory()}. - * @since 2.0 - */ - public BackendRequirement getQueryBackendRequirementType() { - return requirement; - } - - /** - * A suggestion for choosing the query evaluator algorithm. - * - *

- * Returns null iff {@link #getQueryBackendRequirementType()} does not return {@link BackendRequirement#SPECIFIC}; - * in such cases a corresponding default backend is selected inside the engine - */ - public IQueryBackendFactory getQueryBackendFactory() { - return queryBackendFactory; - } - - /** - * Each entry in the immutable map overrides backend-specific options regarding query evaluation. - * - *

The map is non-null, even if empty. - * Null-valued map entries are also permitted to erase hints via {@link #overrideBy(QueryEvaluationHint)}. - * - * @since 1.5 - */ - public Map, Object> getBackendHintSettings() { - return backendHintSettings; - } - - - /** - * Override values in this hint and return a consolidated instance. - * - * @since 1.4 - */ - public QueryEvaluationHint overrideBy(QueryEvaluationHint overridingHint){ - if (overridingHint == null) - return this; - - BackendRequirement overriddenRequirement = this.getQueryBackendRequirementType(); - if (overridingHint.getQueryBackendRequirementType() != BackendRequirement.UNSPECIFIED) { - overriddenRequirement = overridingHint.getQueryBackendRequirementType(); - } - Map, Object> hints = new HashMap<>(this.getBackendHintSettings()); - if (overridingHint.getBackendHintSettings() != null) { - hints.putAll(overridingHint.getBackendHintSettings()); - } - if (overriddenRequirement == BackendRequirement.SPECIFIC) { - IQueryBackendFactory factory = this.getQueryBackendFactory(); - if (overridingHint.getQueryBackendFactory() != null) { - factory = overridingHint.getQueryBackendFactory(); - } - return new QueryEvaluationHint(hints, factory); - } else { - return new QueryEvaluationHint(hints, overriddenRequirement); - } - } - - /** - * Returns whether the given hint option is overridden. - * @since 1.5 - */ - public boolean isOptionOverridden(QueryHintOption option) { - return getBackendHintSettings().containsKey(option); - } - - /** - * Returns the value of the given hint option from the given hint collection, or null if not defined. - * @since 1.5 - */ - @SuppressWarnings("unchecked") - public HintValue getValueOrNull(QueryHintOption option) { - return (HintValue) getBackendHintSettings().get(option); - } - - /** - * Returns the value of the given hint option from the given hint collection, or the default value if not defined. - * Intended to be called by backends to find out the definitive value that should be considered. - * @since 1.5 - */ - public HintValue getValueOrDefault(QueryHintOption option) { - return option.getValueOrDefault(this); - } - - @Override - public int hashCode() { - return Objects.hash(backendHintSettings, queryBackendFactory, requirement); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - QueryEvaluationHint other = (QueryEvaluationHint) obj; - return Objects.equals(backendHintSettings, other.backendHintSettings) - && - Objects.equals(queryBackendFactory, other.queryBackendFactory) - && - Objects.equals(requirement, other.requirement) - ; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - - if (getQueryBackendFactory() != null) - sb.append("backend: ").append(getQueryBackendFactory().getBackendClass().getSimpleName()); - if (! backendHintSettings.isEmpty()) { - sb.append("hints: "); - if(backendHintSettings instanceof AbstractMap){ - sb.append(backendHintSettings.toString()); - } else { - // we have to iterate on the contents - - String joinedHintMap = backendHintSettings.entrySet().stream() - .map(setting -> setting.getKey() + "=" + setting.getValue()).collect(Collectors.joining(", ")); - sb.append('{').append(joinedHintMap).append('}'); - } - } - - final String result = sb.toString(); - return result.isEmpty() ? "defaults" : result; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java deleted file mode 100644 index 2c6bb4de..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java +++ /dev/null @@ -1,122 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -import java.util.Map; -import java.util.Objects; - -/** - * Each instance of this class corresponds to a given hint option. - * - * It is recommended to expose options to clients (and query backends) as public static fields. - * - * @author Gabor Bergmann - * @since 1.5 - */ -public class QueryHintOption { - - private String optionQualifiedName; - private HintValue defaultValue; - - /** - * Instantiates an option object with the given name and default value. - */ - public QueryHintOption(String optionQualifiedName, HintValue defaultValue) { - this.optionQualifiedName = optionQualifiedName; - this.defaultValue = defaultValue; - } - - /** - * This is the recommended constructor for hint options defined as static fields within an enclosing class. - * Combines the qualified name of the hint from the (qualified) name of the enclosing class and a local name (unique within that class). - */ - public QueryHintOption(Class optionNamespace, String optionLocalName, T defaultValue) { - this(String.format("%s@%s", optionLocalName, optionNamespace.getName()), defaultValue); - } - - /** - * Returns the qualified name, a unique string identifier of the option. - */ - public String getQualifiedName() { - return optionQualifiedName; - } - - /** - * Returns the default value of this hint option, which is to be used by query backends in the case no overriding value is assigned. - */ - public HintValue getDefaultValue() { - return defaultValue; - } - - /** - * Returns the value of this hint option from the given hint collection, or the default value if not defined. - * Intended to be called by backends to find out the definitive value that should be considered. - */ - @SuppressWarnings("unchecked") - public HintValue getValueOrDefault(QueryEvaluationHint hints) { - Object value = hints.getValueOrNull(this); - if (value == null) - return getDefaultValue(); - else { - return (HintValue) value; - } - } - - - /** - * Returns the value of this hint option from the given hint collection, or null if not defined. - */ - public HintValue getValueOrNull(QueryEvaluationHint hints) { - return hints.getValueOrNull(this); - } - - /** - * Returns whether this hint option is defined in the given hint collection. - */ - public boolean isOverriddenIn(QueryEvaluationHint hints) { - return hints.isOptionOverridden(this); - } - - /** - * Puts a value of this hint option into an option-to-value map. - * - *

This method is offered in lieu of a builder API. - * Use this method on any number of hint options in order to populate an option-value map. - * Then instantiate the immutable {@link QueryEvaluationHint} using the map. - * - * @see #insertValueIfNondefault(Map, Object) - * @return the hint value that was previously present in the map under this hint option, carrying over the semantics of {@link Map#put(Object, Object)}. - */ - @SuppressWarnings("unchecked") - public HintValue insertOverridingValue(Map, Object> hints, HintValue overridingValue) { - return (HintValue) hints.put(this, overridingValue); - } - - /** - * Puts a value of this hint option into an option-to-value map, if the given value differs from the default value of the option. - * If the default value is provided instead, then the map is not updated. - * - *

This method is offered in lieu of a builder API. - * Use this method on any number of hint options in order to populate an option-value map. - * Then instantiate the immutable {@link QueryEvaluationHint} using the map. - * - * @see #insertOverridingValue(Map, Object) - * @since 2.0 - */ - public void insertValueIfNondefault(Map, Object> hints, HintValue overridingValue) { - if (!Objects.equals(defaultValue, overridingValue)) - hints.put(this, overridingValue); - } - - @Override - public String toString() { - return optionQualifiedName; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java deleted file mode 100644 index 6ec6d53e..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java +++ /dev/null @@ -1,74 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.backend; - -import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; -import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; - -/** - * Uniform way of requesting result providers for pattern calls within queries. - * Intended users are query backends, for calling other backends to deliver results of dependee queries. - * - * @author Gabor Bergmann - * @since 2.1 - */ -public class ResultProviderRequestor { - IQueryBackend callerBackend; - IQueryResultProviderAccess resultProviderAccess; - IQueryBackendHintProvider hintProvider; - ICallDelegationStrategy delegationStrategy; - QueryEvaluationHint callerHint; - QueryEvaluationHint universalOverride; - - - /** - * @param callerBackend the actual backend evaluating the calling pattern. - * @param resultProviderAccess - * @param hintProvider - * @param delegationStrategy - * @param callerHint a hint under which the calling pattern is evaluated, - * @param universalOverride if non-null, overrides the hint with extra options after the {@link ICallDelegationStrategy} - */ - public ResultProviderRequestor(IQueryBackend callerBackend, IQueryResultProviderAccess resultProviderAccess, - IQueryBackendHintProvider hintProvider, ICallDelegationStrategy delegationStrategy, - QueryEvaluationHint callerHint, QueryEvaluationHint universalOverride) { - super(); - this.callerBackend = callerBackend; - this.resultProviderAccess = resultProviderAccess; - this.hintProvider = hintProvider; - this.delegationStrategy = delegationStrategy; - this.callerHint = callerHint; - this.universalOverride = universalOverride; - } - - - - - /** - * - * @param call a {@link PConstraint} in a query that calls another query. - * @param spotOverride if non-null, overrides the hint with extra options after the {@link ICallDelegationStrategy} - * and the universal override specified in the constructor - * @return the obtained result provider - */ - public IQueryResultProvider requestResultProvider(IQueryReference call, QueryEvaluationHint spotOverride) { - QueryEvaluationHint hints = - delegationStrategy.transformHints(call, callerHint, callerBackend, hintProvider); - - if (universalOverride != null) - hints = hints.overrideBy(universalOverride); - - if (spotOverride != null) - hints = hints.overrideBy(spotOverride); - - return resultProviderAccess.getResultProvider(call.getReferredQuery(), hints); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java deleted file mode 100644 index 99611758..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java +++ /dev/null @@ -1,69 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -/** - * Common abstract class for implementers of {@link IQueryMetaContext} - * - * @author Grill Balázs - * @since 1.3 - * - */ -public abstract class AbstractQueryMetaContext implements IQueryMetaContext { - - /** - * @since 2.0 - */ - @Override - public Map> getConditionalImplications(IInputKey implyingKey) { - return new HashMap<>(); - } - - /** - * @since 1.6 - */ - @Override - public boolean canLeadOutOfScope(IInputKey key) { - return key.getArity() > 1; - } - - /** - * @since 1.6 - */ - @Override - public Comparator getSuggestedEliminationOrdering() { - return (o1, o2) -> 0; - } - - /** - * @since 1.6 - */ - @Override - public Collection getWeakenedAlternatives(IInputKey implyingKey) { - return Collections.emptySet(); - } - - @Override - public boolean isPosetKey(IInputKey key) { - return false; - } - - @Override - public IPosetComparator getPosetComparator(Iterable key) { - return null; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java deleted file mode 100644 index c797eff9..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java +++ /dev/null @@ -1,21 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -/** - * This class is intended to be extended by implementors. The main purpose of this abstract implementation to protect - * implementors from future changes in the interface. - * - * @author Grill Balázs - * @since 1.4 - * - */ -public abstract class AbstractQueryRuntimeContext implements IQueryRuntimeContext { - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java deleted file mode 100644 index 4dbcca88..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java +++ /dev/null @@ -1,45 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -/** - * An input key identifies an input (extensional) relation, such as the instance set of a given node or edge type, or the direct containment relation. - * - *

The input key, at the very minimum, is associated with an arity (number of columns), a user-friendly name, and a string identifier (for distributive purposes). - * - *

The input key itself must be an immutable data object that properly overrides equals() and hashCode(). - * It must be instantiable without using the query context object, so that query specifications may construct the appropriate PQueries. - * - * @author Bergmann Gabor - * - */ -public interface IInputKey { - - /** - * A user-friendly name that can be shown on screen for debug purposes, included in exceptions, etc. - */ - public String getPrettyPrintableName(); - /** - * An internal string identifier that can be used to uniquely identify to input key (relevant for distributed applications). - */ - public String getStringID(); - - /** - * The width of tuples in this relation. - */ - public int getArity(); - - /** - * Returns true iff instance tuples of the key can be enumerated. - *

If false, the runtime can only test tuple membership in the extensional relation identified by the key, but not enumerate member tuples in general. - */ - boolean isEnumerable(); - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java deleted file mode 100644 index e2d5bcee..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * Implementations of this interface aid the query engine with the ordering of poset elements. This information is - * particularly important in the delete and re-derive evaluation mode because they let the engine identify monotone - * change pairs. - * - * @author Tamas Szabo - * @since 1.6 - */ -public interface IPosetComparator { - - /** - * Returns true if the 'left' tuple of poset elements is smaller or equal than the 'right' tuple of poset elements according to - * the partial order that this poset comparator employs. - * - * @param left - * the left tuple of poset elements - * @param right - * the right tuple of poset elements - * @return true if left is smaller or equal to right, false otherwise - */ - public boolean isLessOrEqual(final Tuple left, final Tuple right); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java deleted file mode 100644 index 04f00aaa..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -import org.apache.log4j.Logger; -import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; -import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; -import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; -import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * This interface is a collector which holds every API that is provided by the engine to control - * the operation of the backends. - * - * @since 1.5 - * @noimplement This interface is not intended to be implemented by clients. - */ -public interface IQueryBackendContext { - - Logger getLogger(); - - IQueryRuntimeContext getRuntimeContext(); - - IQueryCacheContext getQueryCacheContext(); - - IQueryBackendHintProvider getHintProvider(); - - IQueryResultProviderAccess getResultProviderAccess(); - - QueryAnalyzer getQueryAnalyzer(); - - /** - * @since 2.0 - */ - IMatcherCapability getRequiredMatcherCapability(PQuery query, QueryEvaluationHint overrideHints); - - /** - * @since 1.6 - */ - boolean areUpdatesDelayed(); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java deleted file mode 100644 index 617207f6..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; -import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; -import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * Provides information on already cached queries to query evaluator backends at runtime. - * - * @author Bergmann Gabor - * - */ -public interface IQueryCacheContext { - - /** - * Checks if there already is a caching result provider for the given query. - *

Returns false if called while the caching result provider of the given query is being constructed in the first place. - */ - public boolean isResultCached(PQuery query); - - /** - * Returns a caching result provider for the given query; it must be constructed if it does not exist yet. - *

Caution: behavior undefined if called while the caching result provider of the given query is being constructed. - * Beware of infinite loops. - *

Postcondition: {@link IQueryBackend#isCaching()} returns true for the {@link #getQueryBackend()} of the returned provider - * - * @throws ViatraQueryRuntimeException - */ - public IQueryResultProvider getCachingResultProvider(PQuery query); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java deleted file mode 100644 index 4c22a3cb..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -import java.util.Collection; -import java.util.Comparator; -import java.util.Map; -import java.util.Set; - -/** - * Provides metamodel information (relationship of input keys) to query evaluator backends at runtime and at query planning time. - * - * @noimplement Implementors should extend {@link AbstractQueryMetaContext} instead of directly implementing this interface. - * @author Bergmann Gabor - */ -public interface IQueryMetaContext { - - /** - * Returns true iff instance tuples of the given key can be enumerated. - *

If false, the runtime can only test tuple membership in the extensional relation identified by the key, but not enumerate member tuples in general. - *

Equivalent to {@link IInputKey#isEnumerable()}. - */ - boolean isEnumerable(IInputKey key); - - /** - * Returns true iff the set of instance tuples of the given key is immutable. - *

If false, the runtime provides notifications upon change. - */ - boolean isStateless(IInputKey key); - - /** - * Returns a set of implications (weakened alternatives), - * with a suggestion for the query planner that satisfying them first may help in satisfying the implying key. - *

Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys. - *

Must follow directly or transitively from implications of {@link #getImplications(IInputKey)}. - * @since 1.6 - */ - Collection getWeakenedAlternatives(IInputKey implyingKey); - - /** - * Returns known direct implications, e.g. edge supertypes, edge opposites, node type constraints, etc. - *

Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys. - */ - Collection getImplications(IInputKey implyingKey); - - /** - * Returns known "double dispatch" implications, where the given implying key implies other input keys under certain additional conditions (themselves input keys). - * For example, a "type x, unscoped" input key may imply the "type x, in scope" input key under the condition of the input key "x is in scope" - * - *

Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys (either as the implying key or as the additional condition). - *

Note that symmetry is not required, i.e. the additional conditions do not have to list the same conditional implication. - * @return multi-map, where the keys are additional conditions and the values are input key implications jointly implied by the condition and the given implying key. - * @since 2.0 - */ - Map> getConditionalImplications(IInputKey implyingKey); - - /** - * Returns functional dependencies of the input key expressed in terms of column indices. - * - *

Each entry of the map is a functional dependency rule, where the entry key specifies source columns and the entry value specifies target columns. - */ - Map, Set> getFunctionalDependencies(IInputKey key); - - /** - * For query normalizing, this is the order suggested for trying to eliminate input keys. - * @since 1.6 - */ - Comparator getSuggestedEliminationOrdering(); - - /** - * Tells whether the given {@link IInputKey} is an edge and may lead out of scope. - * - * @since 1.6 - */ - boolean canLeadOutOfScope(IInputKey key); - - /** - * Returns true if the given {@link IInputKey} represents a poset type. - * @since 1.6 - */ - boolean isPosetKey(IInputKey key); - - /** - * Returns an {@link IPosetComparator} for the given set of {@link IInputKey}s. - * - * @param keys an iterable collection of input keys - * @return the poset comparator - * @since 1.6 - */ - IPosetComparator getPosetComparator(Iterable keys); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java deleted file mode 100644 index 7fecd01a..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; -import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * This interface exposes API to request {@link IQueryResultProvider} for {@link PQuery} instances. - * - * @author Grill Balázs - * @since 1.5 - * @noimplement This interface is not intended to be implemented by clients. - */ -public interface IQueryResultProviderAccess { - - /** - * Get a result provider for the given {@link PQuery}, which conforms the capabilities requested by the - * given {@link QueryEvaluationHint} object. - * @throws ViatraQueryRuntimeException - */ - public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint overrideHints); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java deleted file mode 100644 index c2e90614..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java +++ /dev/null @@ -1,281 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -import java.lang.reflect.InvocationTargetException; -import java.util.Optional; -import java.util.concurrent.Callable; - -import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.Accuracy; - -/** - * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime. - * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface. - * - * @author Bergmann Gabor - * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead. - */ -public interface IQueryRuntimeContext { - /** - * Provides metamodel-specific info independent of the runtime instance model. - */ - public IQueryMetaContext getMetaContext(); - - - /** - * The given callable will be executed, and all model traversals will be delayed until the execution is done. If - * there are any outstanding information to be read from the model, a single coalesced model traversal will - * initialize the caches and deliver the notifications. - * - *

Calls may be nested. A single coalesced traversal will happen at the end of the outermost call. - * - *

Caution: results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. - * For example, if a certain input key is not cached yet, an empty relation may be reported during callable.call(); the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. - * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}). - * - * @param callable - */ - public abstract V coalesceTraversals(Callable callable) throws InvocationTargetException; - /** - * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}). - */ - public boolean isCoalescing(); - - /** - * Returns true if index is available for the given key providing the given service. - * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @since 1.4 - */ - public boolean isIndexed(IInputKey key, IndexingService service); - - /** - * If the given (enumerable) input key is not yet indexed, the model will be traversed - * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) - * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging - * multiple indexing requests to an appropriate level. - * - *

Postcondition: After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key - * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false. - * - *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @since 1.4 - */ - public void ensureIndexed(IInputKey key, IndexingService service); - - /** - * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple. - * - * @param key an input key - * @param seedMask - * a mask that extracts those parameters of the input key (from the entire parameter list) that should be - * bound to a fixed value; must not be null. Note: any given index must occur at most once in seedMask. - * @param seed - * the tuple of fixed values restricting the match set to be considered, in the same order as given in - * parameterSeedMask, so that for each considered match tuple, - * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. - * - * @return the number of tuples in the model for the given key and seed - * - *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @since 1.7 - */ - public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed); - - - /** - * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask - * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy. - * - *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. - * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. - * - *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. - * - * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy. - * - * @since 2.1 - */ - public Optional estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy); - - - /** - * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask - * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). - * The estimate must meet the required accuracy. - * - *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. - * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. - * - *

For an empty relation, zero is acceptable as an exact answer. - * - *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. - * - * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy. - * - * @since 2.1 - */ - public default Optional estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { - if (key.isEnumerable()) { - return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, - (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy)); - } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty(); - } - - - /** - * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple. - * - * @param key an input key - * @param seedMask - * a mask that extracts those parameters of the input key (from the entire parameter list) that should be - * bound to a fixed value; must not be null. Note: any given index must occur at most once in seedMask. - * @param seed - * the tuple of fixed values restricting the match set to be considered, in the same order as given in - * parameterSeedMask, so that for each considered match tuple, - * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. - * @return the tuples in the model for the given key and seed - * - *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @since 1.7 - */ - public Iterable enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed); - - /** - * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples - * are bound by the seed except for one. - * - *

- * Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given - * tuple, and then returns the single value from each tuple which is not bound by the ssed mask. - * - * @param key - * an input key - * @param seedMask - * a mask that extracts those parameters of the input key (from the entire parameter list) that should be - * bound to a fixed value; must not be null. Note: any given index must occur at most - * once in seedMask, and seedMask must include all parameters in any arbitrary order except one. - * @param seed - * the tuple of fixed values restricting the match set to be considered, in the same order as given in - * parameterSeedMask, so that for each considered match tuple, - * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. - * @return the objects in the model for the given key and seed - * - *

- * Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @throws IllegalArgumentException - * if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @since 1.7 - */ - public Iterable enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed); - - /** - * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples - * are bound by the seed. - * - *

- * Returns whether the given tuple is in the extensional relation identified by the input key. - * - *

- * Note: this call works for non-enumerable input keys as well. - * - * @param key - * an input key - * @param seed - * the tuple of fixed values restricting the match set to be considered, in the same order as given in - * parameterSeedMask, so that for each considered match tuple, - * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. - * @return true iff there is at least a single tuple contained in the relation that corresponds to the seed tuple - * @since 2.0 - */ - public boolean containsTuple(IInputKey key, ITuple seed); - - - /** - * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple. - *

This should be called after invoking - * - * @param key an input key - * @param seed can be null or a tuple with matching arity; - * if non-null, only those updates in the model are notified about - * that match the seed at positions where the seed is non-null. - * @param listener will be notified of future changes - * - *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - */ - public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); - - /** - * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple. - * - * @param key an input key - * @param seed can be null or a tuple with matching arity; - * if non-null, only those updates in the model are notified about - * that match the seed at positions where the seed is non-null. - * @param listener will no longer be notified of future changes - * - *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. - */ - public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); - /* - TODO: uniqueness - */ - - /** - * Wraps the external element into the internal representation that is to be used by the query backend - *

model element -> internal object. - *

null must be mapped to null. - */ - public Object wrapElement(Object externalElement); - - /** - * Unwraps the internal representation of the element into its original form - *

internal object -> model element - *

null must be mapped to null. - */ - public Object unwrapElement(Object internalElement); - - /** - * Unwraps the tuple of elements into the internal representation that is to be used by the query backend - *

model elements -> internal objects - *

null must be mapped to null. - */ - public Tuple wrapTuple(Tuple externalElements); - - /** - * Unwraps the tuple of internal representations of elements into their original forms - *

internal objects -> model elements - *

null must be mapped to null. - */ - public Tuple unwrapTuple(Tuple internalElements); - - /** - * Starts wildcard indexing for the given service. After this call, no registration is required for this {@link IndexingService}. - * a previously set wildcard level cannot be lowered, only extended. - * @since 1.4 - */ - public void ensureWildcardIndexing(IndexingService service); - - /** - * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as - * the indexing is finished. The callback is executed only once, then is removed from the callback queue. - * @param traversalCallback - * @throws InvocationTargetException - * @since 1.4 - */ - public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException; -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java deleted file mode 100644 index 7be27d56..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * Listens for changes in the runtime context. - * @author Bergmann Gabor - * - */ -public interface IQueryRuntimeContextListener { - - /** - * The given tuple was inserted into or removed from the input relation indicated by the given key. - * @param key the key identifying the input relation that was updated - * @param updateTuple the tuple that was inserted or removed - * @param isInsertion true if it was an insertion, false otherwise. - */ - public void update(IInputKey key, Tuple updateTuple, boolean isInsertion); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java deleted file mode 100644 index 8210765d..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -/** - * These are the different services which can be provided by an {@link IQueryRuntimeContext} implementation. - * - * @author Grill Balázs - * @since 1.4 - * - */ -public enum IndexingService { - - /** - * Cardinality information is available. Makes possible to calculate - * unseeded calls of {@link IQueryRuntimeContext#countTuples(IInputKey, tools.refinery.viatra.runtime.matchers.tuple.Tuple)} - */ - STATISTICS, - - /** - * The indexer can provide notifications about changes in the model. - */ - NOTIFICATIONS, - - /** - * Enables enumeration of instances and reverse-navigation. - */ - INSTANCES - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java deleted file mode 100644 index 2a403810..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java +++ /dev/null @@ -1,103 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Data object representing the implication of an input key, in use cases including edge supertypes, edge opposites, node type constraints, etc. - * - *

Each instance tuple of the implying input key (if given) implies the presence of an instance tuple of the implied input key consisting of elements of the original tuple at given positions. - * When the input key is null, it is not an input constraint but some other source that implies input keys. - * - *

The implication is an immutable data object. - * - * @author Bergmann Gabor - * - */ -public final class InputKeyImplication { - private IInputKey implyingKey; - private IInputKey impliedKey; - private List impliedIndices; - - /** - * Optional. Instance tuples of this input key imply an instance tuple of another key. - * Sometimes it is not an input key that implies other input keys, so this attribute can be null. - */ - public IInputKey getImplyingKey() { - return implyingKey; - } - /** - * An instance tuple of this input key is implied by another key. - */ - public IInputKey getImpliedKey() { - return impliedKey; - } - /** - * The implied instance tuple consists of the values in the implying tuple at these indices. - */ - public List getImpliedIndices() { - return impliedIndices; - } - /** - * @param implyingKey instance tuples of this input key imply an instance tuple of the other key. - * @param impliedKey instance tuple of this input key is implied by the other key. - * @param implyingIndices the implied instance tuple consists of the values in the implying tuple at these indices. - */ - public InputKeyImplication(IInputKey implyingKey, IInputKey impliedKey, - List implyingIndices) { - super(); - this.implyingKey = implyingKey; - this.impliedKey = impliedKey; - this.impliedIndices = Collections.unmodifiableList(new ArrayList<>(implyingIndices)); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((impliedIndices == null) ? 0 : impliedIndices.hashCode()); - result = prime * result - + ((impliedKey == null) ? 0 : impliedKey.hashCode()); - result = prime * result - + ((implyingKey == null) ? 0 : implyingKey.hashCode()); - return result; - } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof InputKeyImplication)) - return false; - InputKeyImplication other = (InputKeyImplication) obj; - if (impliedIndices == null) { - if (other.impliedIndices != null) - return false; - } else if (!impliedIndices.equals(other.impliedIndices)) - return false; - if (impliedKey == null) { - if (other.impliedKey != null) - return false; - } else if (!impliedKey.equals(other.impliedKey)) - return false; - if (implyingKey == null) { - if (other.implyingKey != null) - return false; - } else if (!implyingKey.equals(other.implyingKey)) - return false; - return true; - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java deleted file mode 100644 index f9b05e5b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context.common; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; - - -/** - * An input key that is identified by a single wrapped object and the class of the wrapper. - * @author Bergmann Gabor - * - */ -public abstract class BaseInputKeyWrapper implements IInputKey { - protected Wrapped wrappedKey; - - public BaseInputKeyWrapper(Wrapped wrappedKey) { - super(); - this.wrappedKey = wrappedKey; - } - - public Wrapped getWrappedKey() { - return wrappedKey; - } - - - @Override - public int hashCode() { - return ((wrappedKey == null) ? 0 : wrappedKey.hashCode()); - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(this.getClass().equals(obj.getClass()))) - return false; - BaseInputKeyWrapper other = (BaseInputKeyWrapper) obj; - if (wrappedKey == null) { - if (other.wrappedKey != null) - return false; - } else if (!wrappedKey.equals(other.wrappedKey)) - return false; - return true; - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java deleted file mode 100644 index eb972c2d..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java +++ /dev/null @@ -1,165 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context.common; - - - -/** - * Instance tuples are of form (x), where object x is an instance of the given Java class or its subclasses. - *

Fine print 1: classes with the same name are considered equivalent. - * Can be instantiated with class name, even if the class itself is not loaded yet; but if the class is available, passing it in the constructor is beneficial to avoid classloading troubles. - *

Fine print 2: primitive types (char, etc.) are transparently treated as their wrapper class (Character, etc.). - *

Non-enumerable type, can only be checked. - *

Stateless type (objects can't change their type) - * @author Bergmann Gabor - * -*/ -public class JavaTransitiveInstancesKey extends BaseInputKeyWrapper { - - /** - * The actual Class whose (transitive) instances this relation contains. Can be null at compile time, if only the name is available. - * Can be a primitive. - */ - private Class cachedOriginalInstanceClass; - - /** - * Same as {@link #cachedOriginalInstanceClass}, but primitive classes are replaced with their wrapper classes (e.g. int --> java.lang.Integer). - */ - private Class cachedWrapperInstanceClass; - - /** - * Preferred constructor. - */ - public JavaTransitiveInstancesKey(Class instanceClass) { - this(primitiveTypeToWrapperClass(instanceClass).getName()); - this.cachedOriginalInstanceClass = instanceClass; - } - - /** - * Call this constructor only in contexts where the class itself is not available for loading, e.g. it has not yet been compiled. - */ - public JavaTransitiveInstancesKey(String className) { - super(className); - } - - - /** - * Returns null if class cannot be loaded. - */ - private Class getOriginalInstanceClass() { - if (cachedOriginalInstanceClass == null) { - try { - resolveClassInternal(); - } catch (ClassNotFoundException e) { - // class not yet available at this point - } - } - return cachedOriginalInstanceClass; - } - - - /** - * @return non-null instance class - * @throws ClassNotFoundException - */ - private Class forceGetOriginalInstanceClass() throws ClassNotFoundException { - if (cachedOriginalInstanceClass == null) { - resolveClassInternal(); - } - return cachedOriginalInstanceClass; - } - - /** - * @return non-null instance class, wrapped if primitive class - * @throws ClassNotFoundException - */ - public Class forceGetWrapperInstanceClass() throws ClassNotFoundException { - forceGetOriginalInstanceClass(); - return getWrapperInstanceClass(); - } - /** - * @return non-null instance class, wrapped if primitive class - * @throws ClassNotFoundException - */ - public Class forceGetInstanceClass() throws ClassNotFoundException { - return forceGetWrapperInstanceClass(); - } - - /** - * @return instance class, wrapped if primitive class, null if class cannot be loaded - */ - public Class getWrapperInstanceClass() { - if (cachedWrapperInstanceClass == null) { - cachedWrapperInstanceClass = primitiveTypeToWrapperClass(getOriginalInstanceClass()); - } - return cachedWrapperInstanceClass; - } - /** - * @return instance class, wrapped if primitive class, null if class cannot be loaded - */ - public Class getInstanceClass() { - return getWrapperInstanceClass(); - } - - private void resolveClassInternal() throws ClassNotFoundException { - cachedOriginalInstanceClass = Class.forName(wrappedKey); - } - - @Override - public String getPrettyPrintableName() { - getWrapperInstanceClass(); - return cachedWrapperInstanceClass == null ? wrappedKey == null ? "" : wrappedKey : cachedWrapperInstanceClass.getName(); - } - - @Override - public String getStringID() { - return "javaClass#"+ getPrettyPrintableName(); - } - - @Override - public int getArity() { - return 1; - } - - @Override - public boolean isEnumerable() { - return false; - } - - @Override - public String toString() { - return this.getPrettyPrintableName(); - } - - private static Class primitiveTypeToWrapperClass(Class instanceClass) { - if (instanceClass != null && instanceClass.isPrimitive()) { - if (Void.TYPE.equals(instanceClass)) - return Void.class; - if (Boolean.TYPE.equals(instanceClass)) - return Boolean.class; - if (Character.TYPE.equals(instanceClass)) - return Character.class; - if (Byte.TYPE.equals(instanceClass)) - return Byte.class; - if (Short.TYPE.equals(instanceClass)) - return Short.class; - if (Integer.TYPE.equals(instanceClass)) - return Integer.class; - if (Long.TYPE.equals(instanceClass)) - return Long.class; - if (Float.TYPE.equals(instanceClass)) - return Float.class; - if (Double.TYPE.equals(instanceClass)) - return Double.class; - } - return instanceClass; - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java deleted file mode 100644 index bb24ec8c..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java +++ /dev/null @@ -1,153 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Abel Hegedus, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.context.surrogate; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.util.IProvider; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; -import tools.refinery.viatra.runtime.matchers.util.SingletonInstanceProvider; - -/** - * @author Abel Hegedus - * - */ -public class SurrogateQueryRegistry { - - private Map> registeredSurrogateQueryMap = new HashMap<>(); - private Map> dynamicSurrogateQueryMap = new HashMap<>(); - - /** - * Hidden constructor - */ - private SurrogateQueryRegistry() { - } - - private static final SurrogateQueryRegistry INSTANCE = new SurrogateQueryRegistry(); - - public static SurrogateQueryRegistry instance() { - return INSTANCE; - } - - /** - * - * @param feature - * @param surrogateQuery - * @return the previous surrogate query associated with feature, or null if there was no such query FQN registered - * @throws IllegalArgumentException if feature or surrogateQuery is null - */ - public IProvider registerSurrogateQueryForFeature(IInputKey feature, PQuery surrogateQuery) { - Preconditions.checkArgument(surrogateQuery != null, "Surrogate query must not be null!"); - return registerSurrogateQueryForFeature(feature, new SingletonInstanceProvider(surrogateQuery)); - } - - /** - * - * @param feature - * @param surrogateQuery - * @return the previous surrogate query associated with feature, or null - * if there was no such query registered - * @throws IllegalArgumentException - * if feature or surrogateQuery is null - */ - public IProvider registerSurrogateQueryForFeature(IInputKey feature, IProvider surrogateQueryProvider) { - Preconditions.checkArgument(feature != null, "Feature must not be null!"); - Preconditions.checkArgument(surrogateQueryProvider != null, "Surrogate query must not be null!"); - return registeredSurrogateQueryMap.put(feature, surrogateQueryProvider); - } - - public IProvider addDynamicSurrogateQueryForFeature(IInputKey feature, PQuery surrogateQuery) { - Preconditions.checkArgument(surrogateQuery != null, "Surrogate query FQN must not be null!"); - return addDynamicSurrogateQueryForFeature(feature, new SingletonInstanceProvider(surrogateQuery)); - } - - public IProvider addDynamicSurrogateQueryForFeature(IInputKey feature, IProvider surrogateQuery) { - Preconditions.checkArgument(feature != null, "Feature must not be null!"); - Preconditions.checkArgument(surrogateQuery != null, "Surrogate query FQN must not be null!"); - return dynamicSurrogateQueryMap.put(feature, surrogateQuery); - } - - public IProvider removeDynamicSurrogateQueryForFeature(IInputKey feature) { - Preconditions.checkArgument(feature != null, "Feature must not be null!"); - return dynamicSurrogateQueryMap.remove(feature); - } - - /** - * - * @param feature that may have surrogate query defined, null not allowed - * @return true if the feature has a surrogate query defined - * @throws IllegalArgumentException if feature is null - */ - public boolean hasSurrogateQueryFQN(IInputKey feature) { - Preconditions.checkArgument(feature != null, "Feature must not be null!"); - boolean surrogateExists = dynamicSurrogateQueryMap.containsKey(feature); - if(!surrogateExists){ - surrogateExists = registeredSurrogateQueryMap.containsKey(feature); - } - return surrogateExists; - } - - /** - * - * @param feature for which the surrogate query FQN should be returned - * @return the surrogate query FQN defined for the feature - * @throws IllegalArgumentException if feature is null - * @throws NoSuchElementException if the feature has no surrogate query defined, use {@link #hasSurrogateQueryFQN} to check - */ - public PQuery getSurrogateQuery(IInputKey feature) { - Preconditions.checkArgument(feature != null, "Feature must not be null!"); - IProvider surrogate = dynamicSurrogateQueryMap.get(feature); - if(surrogate == null) { - surrogate = registeredSurrogateQueryMap.get(feature); - } - if(surrogate != null) { - return surrogate.get(); - } else { - throw new NoSuchElementException(String.format("Feature %s has no surrogate query defined! Use #hasSurrogateQueryFQN to check existence.", feature)); - } - } - - /** - * @return an unmodifiable set of features with registered surrogate queries - */ - public Set getRegisteredSurrogateQueries() { - return Collections.unmodifiableSet(getRegisteredSurrogateQueriesInternal()); - } - - private Set getRegisteredSurrogateQueriesInternal() { - return registeredSurrogateQueryMap.keySet(); - } - - /** - * @return an unmodifiable set of features with dynamically added surrogate queries - */ - public Set getDynamicSurrogateQueries() { - return Collections.unmodifiableSet(getDynamicSurrogateQueriesInternal()); - } - - private Set getDynamicSurrogateQueriesInternal() { - return dynamicSurrogateQueryMap.keySet(); - } - - /** - * @return an unmodifiable set that contains all features with surrogate queries. - */ - public Set getAllSurrogateQueries() { - Set results = new HashSet<>(getRegisteredSurrogateQueriesInternal()); - results.addAll(getDynamicSurrogateQueriesInternal()); - return results; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java deleted file mode 100644 index 66587f77..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java +++ /dev/null @@ -1,58 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.memories; - -import java.util.Iterator; -import java.util.Map; - -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; -import tools.refinery.viatra.runtime.matchers.util.IMemory; - -/** - * Common parts of nullary and identity specializations. - * - * @noextend This class is not intended to be subclassed by clients. - * @author Gabor Bergmann - * @since 2.0 - */ -abstract class AbstractTrivialMaskedMemory> extends MaskedTupleMemory { - - protected IMemory tuples; - - protected AbstractTrivialMaskedMemory(TupleMask mask, MemoryType bucketType, Object owner) { - super(mask, owner); - tuples = CollectionsFactory.createMemory(Object.class, bucketType); - } - - @Override - public Map> getWithTimeline(ITuple signature) { - throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); - } - - @Override - public void clear() { - tuples.clear(); - } - - @Override - public int getTotalSize() { - return tuples.size(); - } - - @Override - public Iterator iterator() { - return tuples.iterator(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java deleted file mode 100644 index 92081409..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java +++ /dev/null @@ -1,127 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.memories; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; - -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; - -/** - * @author Gabor Bergmann - * - * Default implementation that covers all cases. - * - * @since 2.0 - */ -public final class DefaultMaskedTupleMemory> - extends MaskedTupleMemory { - /** - * Maps a signature tuple to the bucket of tuples with the given signature. - * - * @since 2.0 - */ - protected IMultiLookup signatureToTuples; - - /** - * @param mask - * The mask used to index the matchings - * @param owner - * the object "owning" this memory - * @param bucketType - * the kind of tuple collection maintained for each indexer bucket - * @since 2.0 - */ - public DefaultMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { - super(mask, owner); - signatureToTuples = CollectionsFactory. createMultiLookup(Object.class, bucketType, Object.class); - } - - @Override - public boolean add(Tuple tuple) { - Tuple signature = mask.transform(tuple); - return add(tuple, signature); - } - - @Override - public boolean add(Tuple tuple, Tuple signature) { - try { - return signatureToTuples.addPair(signature, tuple) == ChangeGranularity.KEY; - } catch (IllegalStateException ex) { // ignore worthless internal exception details - throw raiseDuplicateInsertion(tuple); - } - - } - - @Override - public boolean remove(Tuple tuple) { - Tuple signature = mask.transform(tuple); - return remove(tuple, signature); - } - - @Override - public boolean remove(Tuple tuple, Tuple signature) { - try { - return signatureToTuples.removePair(signature, tuple) == ChangeGranularity.KEY; - } catch (IllegalStateException ex) { // ignore worthless internal exception details - throw raiseDuplicateDeletion(tuple); - } - } - - @Override - public Map> getWithTimeline(ITuple signature) { - throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); - } - - @Override - public Collection get(ITuple signature) { - IMemoryView bucket = signatureToTuples.lookupUnsafe(signature); - return bucket == null ? null : bucket.distinctValues(); - } - - @Override - public void clear() { - signatureToTuples.clear(); - } - - @Override - public Iterable getSignatures() { - return signatureToTuples.distinctKeys(); - } - - @Override - public Iterator iterator() { - return signatureToTuples.distinctValues().iterator(); - } - - @Override - public int getTotalSize() { - int i = 0; - for (Tuple key : signatureToTuples.distinctKeys()) { - i += signatureToTuples.lookup(key).size(); - } - return i; - } - - @Override - public int getKeysetSize() { - return signatureToTuples.countKeys(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java deleted file mode 100644 index dc59daf5..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java +++ /dev/null @@ -1,77 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.memories; - -import java.util.Collection; -import java.util.Collections; - -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; - -/** - * Specialized for identity mask; tuples are stored as a simple set/multiset memory. - * - * @author Gabor Bergmann - * @since 2.0 - */ -public final class IdentityMaskedTupleMemory> extends AbstractTrivialMaskedMemory { - - /** - * @param mask - * The mask used to index the matchings - * @param owner the object "owning" this memory - * @param bucketType the kind of tuple collection maintained for each indexer bucket - * @since 2.0 - */ - public IdentityMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { - super(mask, bucketType, owner); - if (!mask.isIdentity()) throw new IllegalArgumentException(mask.toString()); - } - - @Override - public int getKeysetSize() { - return tuples.size(); - } - - @Override - public Iterable getSignatures() { - return tuples; - } - - @Override - public Collection get(ITuple signature) { - Tuple contained = tuples.theContainedVersionOfUnsafe(signature); - return contained != null ? - Collections.singleton(contained) : - null; - } - - @Override - public boolean remove(Tuple tuple, Tuple signature) { - return tuples.removeOne(tuple); - } - - @Override - public boolean remove(Tuple tuple) { - return tuples.removeOne(tuple); - } - - @Override - public boolean add(Tuple tuple, Tuple signature) { - return tuples.addOne(tuple); - } - - @Override - public boolean add(Tuple tuple) { - return tuples.addOne(tuple); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java deleted file mode 100644 index 62377624..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java +++ /dev/null @@ -1,385 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.memories; - -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Map; - -import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyDefaultMaskedTupleMemory; -import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyIdentityMaskedTupleMemory; -import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyNullaryMaskedTupleMemory; -import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyUnaryMaskedTupleMemory; -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.Clearable; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.resumable.MaskedResumable; -import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; - -/** - * Indexes a collection of tuples by their signature (i.e. footprint, projection) obtained according to a mask. May - * belong to an "owner" (for documentation / traceability purposes). - *

- * There are timeless and timely versions of the different memories. Timely versions associate {@link Timeline}s with - * the stored tuples. - * - * @noextend This class is not intended to be subclassed by clients. - * @author Gabor Bergmann - * @author Tamas Szabo - * @since 2.0 - */ -public abstract class MaskedTupleMemory> - implements Clearable, MaskedResumable { - - /** - * Creates a new memory for the given owner that indexes tuples according to the given mask. - */ - public static > MaskedTupleMemory create(final TupleMask mask, - final MemoryType bucketType, final Object owner) { - return create(mask, bucketType, owner, false); - } - - /** - * Creates a new memory for the given owner that indexes tuples according to the given mask. Clients can specify if - * the created memory should be timely or not.
- *
- * Timely means that tuples are associated with a timeline. - * - * @since 2.3 - */ - public static > MaskedTupleMemory create(final TupleMask mask, - final MemoryType bucketType, final Object owner, final boolean isTimely) { - return create(mask, bucketType, owner, isTimely, false); - } - - /** - * Creates a new memory for the given owner that indexes tuples according to the given mask. Clients can specify if - * the created memory should be timely or not. In case of timely memory, clients can also specify if the memory is - * lazy or not.
- *
- * Timely means that tuples are associated with a timeline.
- *
- * Lazyness can only be used together with timely memories. It means that the maintenance of the timelines is lazy, - * that is, the memory only updates its internal data structures at the timestamp affected by an update, and can be - * instructed later to resume the maintenance at higher timestamps, as well. - * - * @since 2.4 - */ - public static > MaskedTupleMemory create(final TupleMask mask, - final MemoryType bucketType, final Object owner, final boolean isTimely, final boolean isLazy) { - if (isTimely) { - if (bucketType != MemoryType.SETS) { - throw new IllegalArgumentException("Timely memories only support SETS as the bucket type!"); - } - if (mask.isIdentity()) { - return new TimelyIdentityMaskedTupleMemory(mask, owner, isLazy); - } else if (0 == mask.getSize()) { - return new TimelyNullaryMaskedTupleMemory(mask, owner, isLazy); - } else if (1 == mask.getSize()) { - return new TimelyUnaryMaskedTupleMemory(mask, owner, isLazy); - } else { - return new TimelyDefaultMaskedTupleMemory(mask, owner, isLazy); - } - } else { - if (isLazy) { - throw new IllegalArgumentException("Lazy maintenance is only supported by timely memories!"); - } - if (mask.isIdentity()) { - return new IdentityMaskedTupleMemory(mask, bucketType, owner); - } else if (0 == mask.getSize()) { - return new NullaryMaskedTupleMemory(mask, bucketType, owner); - } else if (1 == mask.getSize()) { - return new UnaryMaskedTupleMemory(mask, bucketType, owner); - } else { - return new DefaultMaskedTupleMemory(mask, bucketType, owner); - } - } - } - - @Override - public Map>> resumeAt(final Timestamp timestamp) { - throw new UnsupportedOperationException("This is only supported by lazy timely memory implementations!"); - } - - @Override - public Iterable getResumableSignatures() { - throw new UnsupportedOperationException("This is only supported by lazy timely memory implementations!"); - } - - @Override - public Timestamp getResumableTimestamp() { - return null; - } - - /** - * Initializes the contents of this memory based on the contents of another memory. The default value is associated - * with each tuple in the timely memories. - * - * @since 2.3 - */ - public void initializeWith(final MaskedTupleMemory other, final Timestamp defaultValue) { - throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); - } - - /** - * Returns true if there is any tuple with the given signature that is present at the timestamp +inf, false - * otherwise. - * @since 2.4 - */ - public boolean isPresentAtInfinity(final ITuple signature) { - return get(signature) != null; - } - - /** - * Returns true of this memory is timely, false otherwise. - * - * @since 2.3 - */ - public boolean isTimely() { - return false; - } - - /** - * The mask by which the tuples are indexed. - */ - protected final TupleMask mask; - - /** - * The object "owning" this memory. May be null. - * - * @since 1.7 - */ - protected final Object owner; - - /** - * The node owning this memory. May be null. - * - * @since 2.0 - */ - public Object getOwner() { - return owner; - } - - /** - * The mask according to which tuples are projected and indexed. - * - * @since 2.0 - */ - public TupleMask getMask() { - return mask; - } - - /** - * @return the number of distinct signatures of all stored tuples. - */ - public abstract int getKeysetSize(); - - /** - * @return the total number of distinct tuples stored. Multiple copies of the same tuple, if allowed, are counted as - * one. - * - *

- * This is currently not cached but computed on demand. It is therefore not efficient, and shall only be - * used for debug / profiling purposes. - */ - public abstract int getTotalSize(); - - /** - * Iterates over distinct tuples stored in the memory, regardless of their signatures. - */ - public abstract Iterator iterator(); - - /** - * Retrieves a read-only view of exactly those signatures for which at least one tuple is stored - * - * @since 2.0 - */ - public abstract Iterable getSignatures(); - - /** - * Retrieves tuples that have the specified signature - * - * @return collection of tuples found, null if none - */ - public abstract Collection get(final ITuple signature); - - /** - * Retrieves the tuples and their associated timelines that have the specified signature. - * - * @return the mappings from tuples to timelines, null if there is no mapping for the signature - * @since 2.4 - */ - public abstract Map> getWithTimeline(final ITuple signature); - - /** - * Retrieves tuples that have the specified signature. - * - * @return collection of tuples found, never null - * @since 2.1 - */ - public Collection getOrEmpty(final ITuple signature) { - final Collection result = get(signature); - return result == null ? Collections.emptySet() : result; - } - - /** - * Retrieves tuples with their associated timelines that have the specified signature. - * - * @return map of tuples and timelines found, never null - * @since 2.4 - */ - public Map> getOrEmptyWithTimeline(final ITuple signature) { - final Map> result = getWithTimeline(signature); - return result == null ? Collections.emptyMap() : result; - } - - /** - * Removes a tuple occurrence from the memory with the given signature. - * - * @param tuple - * the tuple to be removed from the memory - * @param signature - * precomputed footprint of the tuple according to the mask - * - * @return true if this was the the last occurrence of the signature (according to the mask) - */ - public boolean remove(final Tuple tuple, final Tuple signature) { - throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); - } - - /** - * Removes a tuple occurrence from the memory with the given signature and timestamp. - * - * @param tuple - * the tuple to be removed from the memory - * @param signature - * precomputed footprint of the tuple according to the mask - * @param timestamp - * the timestamp associated with the tuple - * - * @return A {@link Diff} describing how the timeline of the given tuple changed. - * - * @since 2.4 - */ - public Diff removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { - throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); - } - - /** - * Removes a tuple occurrence from the memory. - * - * @param tuple - * the tuple to be removed from the memory - * - * @return true if this was the the last occurrence of the signature (according to the mask) - */ - public boolean remove(final Tuple tuple) { - throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); - } - - /** - * Removes a tuple occurrence from the memory with the given timestamp. - * - * @param tuple - * the tuple to be removed from the memory - * @param timestamp - * the timestamp associated with the tuple - * - * @return A {@link Diff} describing how the timeline of the given tuple changed. - * - * @since 2.4 - */ - public Diff removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { - throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); - } - - /** - * Adds a tuple occurrence to the memory with the given signature. - * - * @param tuple - * the tuple to be added to the memory - * @param signature - * precomputed footprint of the tuple according to the mask - * - * @return true if new signature encountered (according to the mask) - */ - public boolean add(final Tuple tuple, final Tuple signature) { - throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); - } - - /** - * Adds a tuple occurrence to the memory with the given signature and timestamp. - * - * @param tuple - * the tuple to be added to the memory - * @param signature - * precomputed footprint of the tuple according to the mask - * @param timestamp - * the timestamp associated with the tuple - * - * @return A {@link Diff} describing how the timeline of the given tuple changed. - * - * @since 2.4 - */ - public Diff addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { - throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); - } - - /** - * Adds a tuple occurrence to the memory. - * - * @param tuple - * the tuple to be added to the memory - * - * @return true if new signature encountered (according to the mask) - */ - public boolean add(final Tuple tuple) { - throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); - } - - /** - * Adds a tuple occurrence to the memory with the given timestamp. - * - * @param tuple - * the tuple to be added to the memory - * @param timestamp - * the timestamp associated with the tuple - * - * @return A {@link Diff} describing how the timeline of the given tuple changed. - * - * @since 2.4 - */ - public Diff addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { - throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); - } - - protected MaskedTupleMemory(final TupleMask mask, final Object owner) { - super(); - this.mask = mask; - this.owner = owner; - } - - protected IllegalStateException raiseDuplicateInsertion(final Tuple tuple) { - return new IllegalStateException(String.format("Duplicate insertion of tuple %s into %s", tuple, owner)); - } - - protected IllegalStateException raiseDuplicateDeletion(final Tuple tuple) { - return new IllegalStateException(String.format("Duplicate deletion of tuple %s from %s", tuple, owner)); - } - - @Override - public String toString() { - return getClass().getSimpleName() + "<" + mask + ">@" + owner; - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java deleted file mode 100644 index 7fa9e053..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java +++ /dev/null @@ -1,85 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.memories; - -import java.util.Collection; -import java.util.Collections; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; - -/** - * Specialized for nullary mask; tuples are stored as a simple set/multiset memory. - * - * @author Gabor Bergmann - * @since 2.0 - */ -public final class NullaryMaskedTupleMemory> extends AbstractTrivialMaskedMemory { - - protected static final Set UNIT_RELATION = - Collections.singleton(Tuples.staticArityFlatTupleOf()); - protected static final Set EMPTY_RELATION = - Collections.emptySet(); - /** - * @param mask - * The mask used to index the matchings - * @param owner the object "owning" this memory - * @param bucketType the kind of tuple collection maintained for each indexer bucket - * @since 2.0 - */ - public NullaryMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { - super(mask, bucketType, owner); - if (0 != mask.getSize()) throw new IllegalArgumentException(mask.toString()); - } - - @Override - public int getKeysetSize() { - return tuples.isEmpty() ? 0 : 1; - } - - @Override - public Iterable getSignatures() { - return tuples.isEmpty() ? EMPTY_RELATION : UNIT_RELATION; - } - - @Override - public Collection get(ITuple signature) { - if (0 == signature.getSize()) - return tuples.distinctValues(); - else return null; - } - - @Override - public boolean remove(Tuple tuple, Tuple signature) { - tuples.removeOne(tuple); - return tuples.isEmpty(); - } - - @Override - public boolean remove(Tuple tuple) { - return remove(tuple, null); - } - - @Override - public boolean add(Tuple tuple, Tuple signature) { - boolean wasEmpty = tuples.isEmpty(); - tuples.addOne(tuple); - return wasEmpty; - } - - @Override - public boolean add(Tuple tuple) { - return add(tuple, null); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java deleted file mode 100644 index f34cc9e3..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.memories; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; - -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; - -/** - * Specialized for unary mask; tuples are indexed by a single column as opposed to a projection (signature) tuple. - * - * @author Gabor Bergmann - * @since 2.0 - */ -public final class UnaryMaskedTupleMemory> extends MaskedTupleMemory { - - protected IMultiLookup columnToTuples; - protected final int keyPosition; - - /** - * @param mask - * The mask used to index the matchings - * @param owner the object "owning" this memory - * @param bucketType the kind of tuple collection maintained for each indexer bucket - * @since 2.0 - */ - public UnaryMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { - super(mask, owner); - if (1 != mask.getSize()) throw new IllegalArgumentException(mask.toString()); - - columnToTuples = CollectionsFactory.createMultiLookup( - Object.class, bucketType, Object.class); - keyPosition = mask.indices[0]; - } - - @Override - public void clear() { - columnToTuples.clear(); - } - - @Override - public int getKeysetSize() { - return columnToTuples.countKeys(); - } - - @Override - public int getTotalSize() { - int i = 0; - for (Object key : columnToTuples.distinctKeys()) { - i += columnToTuples.lookup(key).size(); - } - return i; - } - - @Override - public Iterator iterator() { - return columnToTuples.distinctValues().iterator(); - } - - @Override - public Iterable getSignatures() { - return () -> { - Iterator wrapped = columnToTuples.distinctKeys().iterator(); - return new Iterator() { - @Override - public boolean hasNext() { - return wrapped.hasNext(); - } - @Override - public Tuple next() { - Object key = wrapped.next(); - return Tuples.staticArityFlatTupleOf(key); - } - }; - }; - } - - @Override - public Collection get(ITuple signature) { - Object key = signature.get(0); - IMemoryView bucket = columnToTuples.lookup(key); - return bucket == null ? null : bucket.distinctValues(); - } - - @Override - public Map> getWithTimeline(ITuple signature) { - throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); - } - - @Override - public boolean remove(Tuple tuple, Tuple signature) { - return removeInternal(tuple, tuple.get(keyPosition)); - } - - @Override - public boolean remove(Tuple tuple) { - return removeInternal(tuple, tuple.get(keyPosition)); - } - - @Override - public boolean add(Tuple tuple, Tuple signature) { - return addInternal(tuple, tuple.get(keyPosition)); - } - - @Override - public boolean add(Tuple tuple) { - return addInternal(tuple, tuple.get(keyPosition)); - } - - protected boolean addInternal(Tuple tuple, Object key) { - try { - return columnToTuples.addPair(key, tuple) == ChangeGranularity.KEY; - } catch (IllegalStateException ex) { // ignore worthless internal exception details - throw raiseDuplicateInsertion(tuple); - } - } - - protected boolean removeInternal(Tuple tuple, Object key) { - try { - return columnToTuples.removePair(key, tuple) == ChangeGranularity.KEY; - } catch (IllegalStateException ex) { // ignore worthless internal exception details - throw raiseDuplicateDeletion(tuple); - } - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java deleted file mode 100644 index 45ce3a4e..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java +++ /dev/null @@ -1,228 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.memories.timely; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.TreeMap; - -import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.Direction; -import tools.refinery.viatra.runtime.matchers.util.Signed; -import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; -import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; - -/** - * Common parts of timely default and timely unary implementations. - * - * @noextend This class is not intended to be subclassed by clients. - * @author Tamas Szabo - * @since 2.3 - */ -abstract class AbstractTimelyMaskedMemory, KeyType> - extends MaskedTupleMemory { - - protected final TreeMap> foldingStates; - protected final Map> memoryMap; - protected final boolean isLazy; - - public AbstractTimelyMaskedMemory(final TupleMask mask, final Object owner, final boolean isLazy) { - super(mask, owner); - this.isLazy = isLazy; - this.memoryMap = CollectionsFactory.createMap(); - this.foldingStates = this.isLazy ? CollectionsFactory.createTreeMap() : null; - } - - @Override - public void initializeWith(final MaskedTupleMemory other, final Timestamp defaultValue) { - final Iterable signatures = other.getSignatures(); - for (final Tuple signature : signatures) { - if (other.isTimely()) { - final Map> tupleMap = other.getWithTimeline(signature); - for (final Entry> entry : tupleMap.entrySet()) { - for (final Signed signed : entry.getValue().asChangeSequence()) { - if (signed.getDirection() == Direction.DELETE) { - this.removeWithTimestamp(entry.getKey(), signed.getPayload()); - } else { - this.addWithTimestamp(entry.getKey(), signed.getPayload()); - } - } - } - } else { - final Collection tuples = other.get(signature); - for (final Tuple tuple : tuples) { - this.addWithTimestamp(tuple, defaultValue); - } - } - } - } - - public boolean isPresentAtInfinityInteral(KeyType key) { - final TimelyMemory values = this.memoryMap.get(key); - if (values == null) { - return false; - } else { - return values.getCountAtInfinity() != 0; - } - } - - @Override - public void clear() { - this.memoryMap.clear(); - } - - @Override - public int getKeysetSize() { - return this.memoryMap.keySet().size(); - } - - @Override - public int getTotalSize() { - int i = 0; - for (final Entry> entry : this.memoryMap.entrySet()) { - i += entry.getValue().size(); - } - return i; - } - - @Override - public Iterator iterator() { - return this.memoryMap.values().stream().flatMap(e -> e.keySet().stream()).iterator(); - } - - protected Collection getInternal(final KeyType key) { - final TimelyMemory memory = this.memoryMap.get(key); - if (memory == null) { - return null; - } else { - return memory.getTuplesAtInfinity(); - } - } - - public Map> getWithTimestampInternal(final KeyType key) { - final TimelyMemory memory = this.memoryMap.get(key); - if (memory == null) { - return null; - } else { - return memory.asMap(); - } - } - - protected Diff removeInternal(final KeyType key, final Tuple tuple, final Timestamp timestamp) { - Timestamp oldResumableTimestamp = null; - Timestamp newResumableTimestamp = null; - - final TimelyMemory keyMemory = this.memoryMap.get(key); - if (keyMemory == null) { - throw raiseDuplicateDeletion(tuple); - } - - if (this.isLazy) { - oldResumableTimestamp = keyMemory.getResumableTimestamp(); - } - - Diff diff = null; - try { - diff = keyMemory.remove(tuple, timestamp); - } catch (final IllegalStateException e) { - throw raiseDuplicateDeletion(tuple); - } - if (keyMemory.isEmpty()) { - this.memoryMap.remove(key); - } - - if (this.isLazy) { - newResumableTimestamp = keyMemory.getResumableTimestamp(); - if (!Objects.equals(oldResumableTimestamp, newResumableTimestamp)) { - unregisterFoldingState(oldResumableTimestamp, key); - registerFoldingState(newResumableTimestamp, key); - } - } - - return diff; - } - - protected Diff addInternal(final KeyType key, final Tuple tuple, final Timestamp timestamp) { - Timestamp oldResumableTimestamp = null; - Timestamp newResumableTimestamp = null; - - final TimelyMemory keyMemory = this.memoryMap.computeIfAbsent(key, - k -> new TimelyMemory(this.isLazy)); - - if (this.isLazy) { - oldResumableTimestamp = keyMemory.getResumableTimestamp(); - } - - final Diff diff = keyMemory.put(tuple, timestamp); - - if (this.isLazy) { - newResumableTimestamp = keyMemory.getResumableTimestamp(); - if (!Objects.equals(oldResumableTimestamp, newResumableTimestamp)) { - unregisterFoldingState(oldResumableTimestamp, key); - registerFoldingState(newResumableTimestamp, key); - } - } - - return diff; - } - - @Override - public Diff removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { - return removeWithTimestamp(tuple, null, timestamp); - } - - @Override - public Diff addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { - return addWithTimestamp(tuple, null, timestamp); - } - - @Override - public boolean isTimely() { - return true; - } - - protected void registerFoldingState(final Timestamp timestamp, final KeyType key) { - if (timestamp != null) { - this.foldingStates.compute(timestamp, (k, v) -> { - if (v == null) { - v = CollectionsFactory.createSet(); - } - v.add(key); - return v; - }); - } - } - - protected void unregisterFoldingState(final Timestamp timestamp, final KeyType key) { - if (timestamp != null) { - this.foldingStates.compute(timestamp, (k, v) -> { - v.remove(key); - return v.isEmpty() ? null : v; - }); - } - } - - @Override - public Timestamp getResumableTimestamp() { - if (this.foldingStates == null || this.foldingStates.isEmpty()) { - return null; - } else { - return this.foldingStates.firstKey(); - } - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java deleted file mode 100644 index ca06685a..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java +++ /dev/null @@ -1,100 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.memories.timely; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; - -import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.Direction; -import tools.refinery.viatra.runtime.matchers.util.Signed; -import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; -import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; - -/** - * Common parts of timely nullary and timely identity implementations. - * - * @noextend This class is not intended to be subclassed by clients. - * @author Tamas Szabo - * @since 2.3 - */ -abstract class AbstractTimelyTrivialMaskedMemory> extends MaskedTupleMemory { - - protected final TimelyMemory memory; - - protected AbstractTimelyTrivialMaskedMemory(final TupleMask mask, final Object owner, final boolean isLazy) { - super(mask, owner); - this.memory = new TimelyMemory(isLazy); - } - - @Override - public void initializeWith(final MaskedTupleMemory other, final Timestamp defaultValue) { - final Iterable signatures = other.getSignatures(); - for (final Tuple signature : signatures) { - if (other.isTimely()) { - final Map> tupleMap = other.getWithTimeline(signature); - for (final Entry> entry : tupleMap.entrySet()) { - for (final Signed signed : entry.getValue().asChangeSequence()) { - if (signed.getDirection() == Direction.DELETE) { - this.removeWithTimestamp(entry.getKey(), signed.getPayload()); - } else { - this.addWithTimestamp(entry.getKey(), signed.getPayload()); - } - } - } - } else { - final Collection tuples = other.get(signature); - for (final Tuple tuple : tuples) { - this.removeWithTimestamp(tuple, defaultValue); - } - } - } - } - - @Override - public void clear() { - this.memory.clear(); - } - - @Override - public int getTotalSize() { - return this.memory.size(); - } - - @Override - public Iterator iterator() { - return this.memory.keySet().iterator(); - } - - @Override - public Diff removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { - return removeWithTimestamp(tuple, null, timestamp); - } - - @Override - public Diff addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { - return addWithTimestamp(tuple, null, timestamp); - } - - @Override - public boolean isTimely() { - return true; - } - - @Override - public Timestamp getResumableTimestamp() { - return this.memory.getResumableTimestamp(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java deleted file mode 100644 index 623d7399..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.memories.timely; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; -import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; - -/** - * Default timely implementation that covers all cases. - * - * @author Tamas Szabo - * @since 2.3 - */ -public final class TimelyDefaultMaskedTupleMemory> - extends AbstractTimelyMaskedMemory { - - public TimelyDefaultMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { - super(mask, owner, isLazy); - } - - @Override - public Iterable getSignatures() { - return this.memoryMap.keySet(); - } - - @Override - public Diff removeWithTimestamp(final Tuple tuple, final Tuple signature, - final Timestamp timestamp) { - final Tuple key = mask.transform(tuple); - return removeInternal(key, tuple, timestamp); - } - - @Override - public Diff addWithTimestamp(final Tuple tuple, final Tuple signature, - final Timestamp timestamp) { - final Tuple key = this.mask.transform(tuple); - return addInternal(key, tuple, timestamp); - } - - @Override - public Collection get(final ITuple signature) { - return getInternal(signature.toImmutable()); - } - - @Override - public Map> getWithTimeline(final ITuple signature) { - return getWithTimestampInternal(signature.toImmutable()); - } - - @Override - public boolean isPresentAtInfinity(final ITuple signature) { - return isPresentAtInfinityInteral(signature.toImmutable()); - } - - @Override - public Set getResumableSignatures() { - if (this.foldingStates == null || this.foldingStates.isEmpty()) { - return Collections.emptySet(); - } else { - return this.foldingStates.firstEntry().getValue(); - } - } - - @Override - public Map>> resumeAt(final Timestamp timestamp) { - final Map>> result = CollectionsFactory.createMap(); - final Timestamp resumableTimestamp = this.getResumableTimestamp(); - if (resumableTimestamp == null || resumableTimestamp.compareTo(timestamp) != 0) { - throw new IllegalStateException("Expected to continue folding at " + resumableTimestamp + "!"); - } - final Set signatures = this.foldingStates.remove(timestamp); - for (final Tuple signature : signatures) { - final TimelyMemory memory = this.memoryMap.get(signature); - final Map> diffMap = memory.resumeAt(resumableTimestamp); - result.put(signature, diffMap); - registerFoldingState(memory.getResumableTimestamp(), signature); - } - return result; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java deleted file mode 100644 index 568f274d..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java +++ /dev/null @@ -1,106 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.memories.timely; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; - -/** - * Timely specialization for identity mask. - * - * @author Tamas Szabo - * @since 2.3 - */ -public final class TimelyIdentityMaskedTupleMemory> - extends AbstractTimelyTrivialMaskedMemory { - - public TimelyIdentityMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { - super(mask, owner, isLazy); - if (!mask.isIdentity()) - throw new IllegalArgumentException(mask.toString()); - } - - @Override - public int getKeysetSize() { - return this.memory.size(); - } - - @Override - public Iterable getSignatures() { - return this.memory.keySet(); - } - - @Override - public Collection get(final ITuple signature) { - if (this.memory.getTuplesAtInfinity().contains(signature)) { - return Collections.singleton(signature.toImmutable()); - } else { - return null; - } - } - - @Override - public Map> getWithTimeline(final ITuple signature) { - final Timeline value = this.memory.get(signature); - if (value != null) { - return Collections.singletonMap(signature.toImmutable(), value); - } else { - return null; - } - } - - @Override - public Diff removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { - try { - return this.memory.remove(tuple, timestamp); - } catch (final IllegalStateException e) { - throw raiseDuplicateDeletion(tuple); - } - } - - @Override - public Diff addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { - return this.memory.put(tuple, timestamp); - } - - @Override - public boolean isPresentAtInfinity(final ITuple signature) { - return this.memory.isPresentAtInfinity(signature.toImmutable()); - } - - @Override - public Set getResumableSignatures() { - if (this.memory.getResumableTimestamp() != null) { - return this.memory.getResumableTuples(); - } else { - return Collections.emptySet(); - } - } - - @Override - public Map>> resumeAt(final Timestamp timestamp) { - final Map> diffMap = this.memory.resumeAt(timestamp); - final Map>> result = CollectionsFactory.createMap(); - for (final Entry> entry : diffMap.entrySet()) { - result.put(entry.getKey(), Collections.singletonMap(entry.getKey(), entry.getValue())); - } - return result; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java deleted file mode 100644 index 75987a89..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java +++ /dev/null @@ -1,108 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.memories.timely; - -import java.util.Collection; -import java.util.Collections; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; -import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; - -/** - * Timely specialization for nullary mask. - * - * @author Tamas Szabo - * @since 2.3 - */ -public final class TimelyNullaryMaskedTupleMemory> - extends AbstractTimelyTrivialMaskedMemory { - - protected static final Tuple EMPTY_TUPLE = Tuples.staticArityFlatTupleOf(); - protected static final Set UNIT_RELATION = Collections.singleton(EMPTY_TUPLE); - protected static final Set EMPTY_RELATION = Collections.emptySet(); - - public TimelyNullaryMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { - super(mask, owner, isLazy); - if (0 != mask.getSize()) { - throw new IllegalArgumentException(mask.toString()); - } - } - - @Override - public int getKeysetSize() { - return this.memory.isEmpty() ? 0 : 1; - } - - @Override - public Iterable getSignatures() { - return this.memory.isEmpty() ? EMPTY_RELATION : UNIT_RELATION; - } - - @Override - public Collection get(final ITuple signature) { - if (0 == signature.getSize()) { - return this.memory.getTuplesAtInfinity(); - } else { - return null; - } - } - - @Override - public Map> getWithTimeline(final ITuple signature) { - if (0 == signature.getSize()) { - return this.memory.asMap(); - } else { - return null; - } - } - - @Override - public Diff removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { - try { - return this.memory.remove(tuple, timestamp); - } catch (final IllegalStateException e) { - throw raiseDuplicateDeletion(tuple); - } - } - - @Override - public Diff addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { - return this.memory.put(tuple, timestamp); - } - - @Override - public boolean isPresentAtInfinity(final ITuple signature) { - if (0 == signature.getSize()) { - return this.memory.getCountAtInfinity() > 0; - } else { - return false; - } - } - - @Override - public Set getResumableSignatures() { - if (this.memory.getResumableTimestamp() != null) { - return UNIT_RELATION; - } else { - return EMPTY_RELATION; - } - } - - @Override - public Map>> resumeAt(final Timestamp timestamp) { - return Collections.singletonMap(EMPTY_TUPLE, this.memory.resumeAt(timestamp)); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java deleted file mode 100644 index 178193af..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java +++ /dev/null @@ -1,133 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.memories.timely; - -import java.util.Collection; -import java.util.Collections; -import java.util.Iterator; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; -import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; - -/** - * Timely specialization for unary mask. - * - * @author Tamas Szabo - * @since 2.3 - */ -public final class TimelyUnaryMaskedTupleMemory> - extends AbstractTimelyMaskedMemory { - - protected final int keyPosition; - - public TimelyUnaryMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { - super(mask, owner, isLazy); - if (1 != mask.getSize()) - throw new IllegalArgumentException(mask.toString()); - this.keyPosition = mask.indices[0]; - } - - @Override - public Iterable getSignatures() { - return () -> { - final Iterator wrapped = this.memoryMap.keySet().iterator(); - return new Iterator() { - @Override - public boolean hasNext() { - return wrapped.hasNext(); - } - - @Override - public Tuple next() { - final Object key = wrapped.next(); - return Tuples.staticArityFlatTupleOf(key); - } - }; - }; - } - - @Override - public Diff removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { - final Object key = tuple.get(keyPosition); - return removeInternal(key, tuple, timestamp); - } - - @Override - public Diff addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { - final Object key = tuple.get(keyPosition); - return addInternal(key, tuple, timestamp); - } - - @Override - public Collection get(final ITuple signature) { - return getInternal(signature.get(0)); - } - - @Override - public Map> getWithTimeline(final ITuple signature) { - return getWithTimestampInternal(signature.get(0)); - } - - @Override - public boolean isPresentAtInfinity(ITuple signature) { - return isPresentAtInfinityInteral(signature.get(0)); - } - - @Override - public Iterable getResumableSignatures() { - if (this.foldingStates == null || this.foldingStates.isEmpty()) { - return Collections.emptySet(); - } else { - return () -> { - final Iterator wrapped = this.foldingStates.firstEntry().getValue().iterator(); - return new Iterator() { - @Override - public boolean hasNext() { - return wrapped.hasNext(); - } - - @Override - public Tuple next() { - final Object key = wrapped.next(); - return Tuples.staticArityFlatTupleOf(key); - } - }; - }; - } - } - - @Override - public Map>> resumeAt(final Timestamp timestamp) { - final Map>> result = CollectionsFactory.createMap(); - final Timestamp resumableTimestamp = this.getResumableTimestamp(); - if (resumableTimestamp == null) { - throw new IllegalStateException("There is nothing to fold!"); - } else if (resumableTimestamp.compareTo(timestamp) != 0) { - throw new IllegalStateException("Expected to continue folding at " + resumableTimestamp + "!"); - } - - final Set signatures = this.foldingStates.remove(timestamp); - for (final Object signature : signatures) { - final TimelyMemory memory = this.memoryMap.get(signature); - final Map> diffMap = memory.resumeAt(resumableTimestamp); - result.put(Tuples.staticArityFlatTupleOf(signature), diffMap); - registerFoldingState(memory.getResumableTimestamp(), signature); - } - return result; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java deleted file mode 100644 index c9f4b305..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java +++ /dev/null @@ -1,108 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.planning; - -import java.util.Map; - -import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; -import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; - -/** - * - * An implicit common parameter is the "effort" PatternDescription. This - * indicates that the build request is part of an effort to build the matcher of - * the given pattern; it it important to record this during code generation so - * that the generated code can be separated according to patterns. - * - * @param - * the handle of a receiver-like RETE ending to which plans can be - * connected - * @author Gabor Bergmann - * @noimplement This interface is not intended to be implemented by clients. - */ -public interface IOperationCompiler { - - /** - * @throws ViatraQueryRuntimeException - */ - public Collector patternCollector(PQuery pattern); - - public void buildConnection(SubPlan parentPlan, Collector collector); - - /** - * @since 0.9 - */ - public void patternFinished(PQuery pattern, Collector collector); - - /** - * @throws ViatraQueryRuntimeException - */ - public SubPlan patternCallPlan(Tuple nodes, PQuery supplierKey); - - public SubPlan transitiveInstantiationPlan(Tuple nodes); - - public SubPlan directInstantiationPlan(Tuple nodes); - - public SubPlan transitiveGeneralizationPlan(Tuple nodes); - - public SubPlan directGeneralizationPlan(Tuple nodes); - - public SubPlan transitiveContainmentPlan(Tuple nodes); - - public SubPlan directContainmentPlan(Tuple nodes); - - public SubPlan binaryEdgeTypePlan(Tuple nodes, Object supplierKey); - - public SubPlan ternaryEdgeTypePlan(Tuple nodes, Object supplierKey); - - public SubPlan unaryTypePlan(Tuple nodes, Object supplierKey); - - public SubPlan buildStartingPlan(Object[] constantValues, Object[] constantNames); - - public SubPlan buildEqualityChecker(SubPlan parentPlan, int[] indices); - - public SubPlan buildInjectivityChecker(SubPlan parentPlan, int subject, int[] inequalIndices); - - public SubPlan buildTransitiveClosure(SubPlan parentPlan); - - public SubPlan buildTrimmer(SubPlan parentPlan, TupleMask trimMask, boolean enforceUniqueness); - - public SubPlan buildBetaNode(SubPlan primaryPlan, SubPlan sidePlan, - TupleMask primaryMask, TupleMask sideMask, TupleMask complementer, boolean negative); - - public SubPlan buildCounterBetaNode(SubPlan primaryPlan, SubPlan sidePlan, - TupleMask primaryMask, TupleMask originalSideMask, TupleMask complementer, - Object aggregateResultCalibrationElement); - - public SubPlan buildCountCheckBetaNode(SubPlan primaryPlan, SubPlan sidePlan, - TupleMask primaryMask, TupleMask originalSideMask, int resultPositionInSignature); - - public SubPlan buildPredicateChecker(IExpressionEvaluator evaluator, Map tupleNameMap, - SubPlan parentPlan); - public SubPlan buildFunctionEvaluator(IExpressionEvaluator evaluator, Map tupleNameMap, - SubPlan parentPlan, Object computedResultCalibrationElement); - - /** - * @return an operation compiler that potentially acts on a separate container - */ - public IOperationCompiler getNextContainer(); - - /** - * @return an operation compiler that puts build actions on the tab of the given pattern - * @since 0.9 - */ - public IOperationCompiler putOnTab(PQuery effort /*, IPatternMatcherContext context*/); - - public void reinitialize(); - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java deleted file mode 100644 index 6ce9d91b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.planning; - -import org.apache.log4j.Logger; -import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; - -/** - * An algorithm that builds a query plan based on a PSystem representation of a body of constraints. This interface is - * for internal use of the various query backends. - * - * @author Gabor Bergmann - */ -public interface IQueryPlannerStrategy { - - /** - * @throws ViatraQueryRuntimeException - */ - public SubPlan plan(PBody pSystem, Logger logger, IQueryMetaContext context); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java deleted file mode 100644 index 501ddf73..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.planning; - -import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; - -/** - * @author Zoltan Ujhelyi - * @since 0.9 - */ -public class QueryProcessingException extends ViatraQueryRuntimeException { - - private static final long serialVersionUID = -8272290113656867086L; - /** - * Binding the '{n}' (n = 1..N) strings to contextual conditions in 'context' - * - * @param context - * : array of context-sensitive Strings - */ - protected static String bind(String message, String[] context) { - if (context == null) - return message; - - String internal = message; - for (int i = 0; i < context.length; i++) { - internal = internal.replace("{" + (i + 1) + "}", context[i] != null ? context[i] : "<>"); - } - return internal; - } - - private Object patternDescription; - private String shortMessage; - - /** - * @param message - * The template of the exception message - * @param context - * The data elements to be used to instantiate the template. Can be null if no context parameter is - * defined - * @param patternDescription - * the PatternDescription where the exception occurred - * @since 2.0 - */ - public QueryProcessingException(String message, Object patternDescription) { - super(message); - initializeFields(message, patternDescription); - } - - /** - * @param message - * The template of the exception message - * @param context - * The data elements to be used to instantiate the template. Can be null if no context parameter is - * defined - * @param patternDescription - * the PatternDescription where the exception occurred - */ - public QueryProcessingException(String message, String[] context, String shortMessage, Object patternDescription) { - super(bind(message, context)); - initializeFields(shortMessage, patternDescription); - } - - /** - * @param message - * The template of the exception message - * @param context - * The data elements to be used to instantiate the template. Can be null if no context parameter is - * defined - * @param patternDescription - * the PatternDescription where the exception occurred - */ - public QueryProcessingException(String message, String[] context, String shortMessage, Object patternDescription, - Throwable cause) { - super(bind(message, context), cause); - initializeFields(shortMessage, patternDescription); - } - - public Object getPatternDescription() { - return patternDescription; - } - - public String getShortMessage() { - return shortMessage; - } - - private void initializeFields(String shortMessage, Object patternDescription) { - this.patternDescription = patternDescription; - this.shortMessage = shortMessage; - } - - - public void setPatternDescription(Object patternDescription) { - this.patternDescription = patternDescription; - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java deleted file mode 100644 index 1998df9d..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java +++ /dev/null @@ -1,240 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.planning; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.stream.Collectors; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; -import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; -import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; -import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; - -/** - * A plan representing a subset of (or possibly all the) constraints evaluated. A SubPlan instance is responsible for - * representing a state of the plan; but after it is initialized it is expected be immutable - * (exception: inferred constraints, see {@link #inferConstraint(PConstraint)}). - * - *

A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. - * Important maintained information:

    - *
  • set of variables whose values are known when the runtime evaluation is at this stage, - *
  • set of constraints that are known to hold true at this point. - *
- * - *

Recommended to instantiate via a {@link SubPlanFactory} or subclasses, - * so that query planners can subclass SubPlan if needed. - * - * @author Gabor Bergmann - * - */ -public class SubPlan { - private PBody body; - private List parentPlans; - private POperation operation; - - private final Set visibleVariables; - private final Set allVariables; - private final Set introducedVariables; // delta compared to first parent - private Set allConstraints; - private Set deltaConstraints; // delta compared to all parents - private Set externallyInferredConstraints; // inferred in addition to direct consequences of the operation and parents - - - - - - /** - * A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. - */ - public SubPlan(PBody body, POperation operation, SubPlan... parentPlans) { - this(body, operation, Arrays.asList(parentPlans)); - } - /** - * A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. - */ - public SubPlan(PBody body, POperation operation, List parentPlans) { - super(); - this.body = body; - this.parentPlans = parentPlans; - this.operation = operation; - - this.externallyInferredConstraints = new HashSet(); - this.deltaConstraints = new HashSet(operation.getDeltaConstraints()); - - this.allVariables = new HashSet(); - for (PConstraint constraint: deltaConstraints) { - this.allVariables.addAll(constraint.getDeducedVariables()); - } - this.allConstraints = new HashSet(deltaConstraints); - for (SubPlan parentPlan: parentPlans) { - this.allConstraints.addAll(parentPlan.getAllEnforcedConstraints()); - this.allVariables.addAll(parentPlan.getAllDeducedVariables()); - } - - // TODO this is ugly a bit - if (operation instanceof PStart) { - this.visibleVariables = new HashSet(((PStart) operation).getAPrioriVariables()); - this.allVariables.addAll(visibleVariables); - } else if (operation instanceof PProject) { - this.visibleVariables = new HashSet(((PProject) operation).getToVariables()); - } else { - this.visibleVariables = new HashSet(); - for (SubPlan parentPlan: parentPlans) - this.visibleVariables.addAll(parentPlan.getVisibleVariables()); - for (PConstraint constraint: deltaConstraints) - this.visibleVariables.addAll(constraint.getDeducedVariables()); - } - - this.introducedVariables = new HashSet(this.visibleVariables); - if (!parentPlans.isEmpty()) - introducedVariables.removeAll(parentPlans.get(0).getVisibleVariables()); - - operation.checkConsistency(this); - } - - - @Override - public String toString() { - return toLongString(); - } - public String toShortString() { - return String.format("Plan{%s}:%s", - visibleVariables.stream().map(PVariable::getName).collect(Collectors.joining(",")), - operation.getShortName()); - } - public String toLongString() { - return String.format("%s<%s>", - toShortString(), - parentPlans.stream().map(Object::toString).collect(Collectors.joining("; "))); - } - - - /** - * All constraints that are known to hold at this point - */ - public Set getAllEnforcedConstraints() { - return allConstraints; - } - - /** - * The new constraints enforced at this stage of plan, that aren't yet enforced at parents - * (results are also included in {@link SubPlan#getAllEnforcedConstraints()}) - */ - public Set getDeltaEnforcedConstraints() { - return deltaConstraints; - } - - /** - * Indicate that a given constraint was found to be automatically satisfied at this point - * without additional operations. - * (results are also included in {@link SubPlan#getDeltaEnforcedConstraints()}) - * - *

Warning: not propagated automatically to child plans, - * so best to invoke before constructing further SubPlans.

- */ - public void inferConstraint(PConstraint constraint) { - externallyInferredConstraints.add(constraint); - deltaConstraints.add(constraint); - allConstraints.add(constraint); - } - - public PBody getBody() { - return body; - } - - /** - * Variables which are assigned a value at this point - * (results are also included in {@link SubPlan#getAllDeducedVariables()}) - */ - public Set getVisibleVariables() { - return visibleVariables; - } - /** - * Variables which have been assigned a value; - * includes visible variables (see {@link #getVisibleVariables()}) - * and additionally any variables hidden by a projection (see {@link PProject}). - */ - public Set getAllDeducedVariables() { - return allVariables; - } - /** - * Delta compared to first parent: variables that are visible here but were not visible at the first parent. - */ - public Set getIntroducedVariables() { - return introducedVariables; - } - public List getParentPlans() { - return parentPlans; - } - public POperation getOperation() { - return operation; - } - - - /** - * The closure of all type judgments of enforced constraints at this point. - *

No subsumption applied. - */ - public Set getAllImpliedTypeJudgements(IQueryMetaContext context) { - Set impliedJudgements = allImpliedTypeJudgements.get(context); - if (impliedJudgements == null) { - Set equivalentJudgements = TypeHelper.getDirectJudgements(getAllEnforcedConstraints(), context); - impliedJudgements = TypeHelper.typeClosure(equivalentJudgements, context); - - allImpliedTypeJudgements.put(context, impliedJudgements); - } - return impliedJudgements; - } - private WeakHashMap> allImpliedTypeJudgements = new WeakHashMap>(); - - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((operation == null) ? 0 : operation.hashCode()); - result = prime * result - + ((parentPlans == null) ? 0 : parentPlans.hashCode()); - return result; - } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof SubPlan)) - return false; - SubPlan other = (SubPlan) obj; - if (operation == null) { - if (other.operation != null) - return false; - } else if (!operation.equals(other.operation)) - return false; - if (parentPlans == null) { - if (other.parentPlans != null) - return false; - } else if (!parentPlans.equals(other.parentPlans)) - return false; - return true; - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java deleted file mode 100644 index d0df5fac..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.planning; - -import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; - -/** - * Single entry point for creating subplans. - * Can be subclassed by query planner to provide specialized SubPlans. - * @author Bergmann Gabor - * - */ -public class SubPlanFactory { - - protected PBody body; - - public SubPlanFactory(PBody body) { - super(); - this.body = body; - } - - public SubPlan createSubPlan(POperation operation, SubPlan... parentPlans) { - return new SubPlan(body, operation, parentPlans); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java deleted file mode 100644 index ed5d1cbb..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java +++ /dev/null @@ -1,165 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.planning.helpers; - -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; -import tools.refinery.viatra.runtime.matchers.planning.SubPlan; -import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; -import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; - -/** - * @author Gabor Bergmann - * - */ -public class BuildHelper { - - private BuildHelper() { - // Hiding constructor for utility class - } - -// public static SubPlan naturalJoin(IOperationCompiler buildable, -// SubPlan primaryPlan, SubPlan secondaryPlan) { -// JoinHelper joinHelper = new JoinHelper(primaryPlan, secondaryPlan); -// return buildable.buildBetaNode(primaryPlan, secondaryPlan, joinHelper.getPrimaryMask(), -// joinHelper.getSecondaryMask(), joinHelper.getComplementerMask(), false); -// } - - - /** - * Reduces the number of tuples by trimming (existentially quantifying) the set of variables that

    - *
  • are visible in the subplan, - *
  • are not exported parameters, - *
  • have all their constraints already enforced in the subplan, - *
and thus will not be needed anymore. - * - * @param onlyIfNotDetermined if true, no trimming performed unless there is at least one variable that is not functionally determined - * @return the plan after the trimming (possibly the original) - * @since 1.5 - */ - public static SubPlan trimUnneccessaryVariables(SubPlanFactory planFactory, /*IOperationCompiler buildable,*/ - SubPlan plan, boolean onlyIfNotDetermined, QueryAnalyzer analyzer) { - Set canBeTrimmed = new HashSet(); - Set variablesInPlan = plan.getVisibleVariables(); - for (PVariable trimCandidate : variablesInPlan) { - if (trimCandidate.getReferringConstraintsOfType(ExportedParameter.class).isEmpty()) { - if (plan.getAllEnforcedConstraints().containsAll(trimCandidate.getReferringConstraints())) - canBeTrimmed.add(trimCandidate); - } - } - final Set retainedVars = setMinus(variablesInPlan, canBeTrimmed); - if (!canBeTrimmed.isEmpty() && !(onlyIfNotDetermined && areVariablesDetermined(plan, retainedVars, canBeTrimmed, analyzer, false))) { - // TODO add smart ordering? - plan = planFactory.createSubPlan(new PProject(retainedVars), plan); - } - return plan; - } - - /** - * @return true iff a set of given variables functionally determine all visible variables in the subplan according to the subplan's constraints - * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; - * if false, user-provided soft dependencies are included as well, that are anticipated but not guaranteed by the storage mechanism; - * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance - * @since 1.5 - */ - public static boolean areAllVariablesDetermined(SubPlan plan, Collection determining, QueryAnalyzer analyzer, boolean strict) { - return areVariablesDetermined(plan, determining, plan.getVisibleVariables(), analyzer, strict); - } - - /** - * @return true iff one set of given variables functionally determine the other set according to the subplan's constraints - * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; - * if false, user-provided soft dependencies are included as well, that are anticipated but not guaranteed by the storage mechanism; - * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance - * @since 1.5 - */ - public static boolean areVariablesDetermined(SubPlan plan, Collection determining, Collection determined, - QueryAnalyzer analyzer, boolean strict) { - Map, Set> dependencies = analyzer.getFunctionalDependencies(plan.getAllEnforcedConstraints(), strict); - final Set closure = FunctionalDependencyHelper.closureOf(determining, dependencies); - final boolean isDetermined = closure.containsAll(determined); - return isDetermined; - } - - private static Set setMinus(Set a, Set b) { - Set difference = new HashSet(a); - difference.removeAll(b); - return difference; - } - - /** - * Finds an arbitrary constraint that is not enforced at the given plan. - * - * @param pSystem - * @param plan - * @return a PConstraint that is not enforced, if any, or null if all are enforced - */ - public static PConstraint getAnyUnenforcedConstraint(PBody pSystem, - SubPlan plan) { - Set allEnforcedConstraints = plan.getAllEnforcedConstraints(); - Set constraints = pSystem.getConstraints(); - for (PConstraint pConstraint : constraints) { - if (!allEnforcedConstraints.contains(pConstraint)) - return pConstraint; - } - return null; - } - - /** - * Skips the last few steps, if any, that are projections, so that a custom projection can be added instead. - * Useful for connecting body final plans into the production node. - * - * @since 2.1 - */ - public static SubPlan eliminateTrailingProjections(SubPlan plan) { - while (plan.getOperation() instanceof PProject) - plan = plan.getParentPlans().get(0); - return plan; - } - - /** - * Verifies whether all constraints are enforced and exported parameters are present. - * - * @param pSystem - * @param plan - * @throws ViatraQueryRuntimeException - */ - public static void finalCheck(final PBody pSystem, SubPlan plan, IQueryMetaContext context) { - PConstraint unenforcedConstraint = getAnyUnenforcedConstraint(pSystem, plan); - if (unenforcedConstraint != null) { - throw new QueryProcessingException( - "Pattern matcher construction terminated without successfully enforcing constraint {1}." - + " Could be caused if the value of some variables can not be deduced, e.g. by circularity of pattern constraints.", - new String[] { unenforcedConstraint.toString() }, "Could not enforce a pattern constraint", null); - } - for (ExportedParameter export : pSystem - .getConstraintsOfType(ExportedParameter.class)) { - if (!export.isReadyAt(plan, context)) { - throw new QueryProcessingException( - "Exported pattern parameter {1} could not be deduced during pattern matcher construction." - + " A pattern constraint is required to positively deduce its value.", - new String[] { export.getParameterName() }, "Could not calculate pattern parameter", - null); - } - } - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java deleted file mode 100644 index 40835f52..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java +++ /dev/null @@ -1,143 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Adam Dudas, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.planning.helpers; - -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.util.Sets; - -/** - * Helper utility class for functional dependency analysis. - * - * Throughout this class attribute sets are represented as generic sets and functional dependencies as maps from - * attribute set (generic sets) to attribute set (generic sets) - * - * @author Adam Dudas - * - */ -public class FunctionalDependencyHelper { - - private FunctionalDependencyHelper() { - // Hiding constructor for utility class - } - - /** - * Get the closure of the specified attribute set relative to the specified functional dependencies. - * - * @param attributes - * The attributes to get the closure of. - * @param dependencies - * The functional dependencies of which the closure operation is relative to. - * @return The closure of the specified attribute set relative to the specified functional dependencies. - */ - public static Set closureOf(Collection attributes, Map, Set> dependencies) { - Set closureSet = new HashSet(); - - for (Set closureSet1 = new HashSet(attributes); closureSet.addAll(closureSet1);) { - closureSet1 = new HashSet(); - for (Entry, Set> dependency : dependencies.entrySet()) { - if (closureSet.containsAll(dependency.getKey())) - closureSet1.addAll(dependency.getValue()); - } - } - - return closureSet; - } - - /** - * @return true if the dependency from the left set to the right set is trivial - * @since 1.5 - */ - public static boolean isTrivial(Set left, Set right) { - return left.containsAll(right); - } - - /*** - * Returns the dependency set over attributes in {@link targetAttributes} that are implied by a given source dependency set. - *

Note: exponential in the size of the target attribute set. - *

Note: minimality of the returned dependency set is currently not guaranteed. - * @param originalDependencies all dependencies that are known to hold on a wider set of attributes - * @param targetAttributes the set of attributes we are interested in - * @since 1.5 - */ - public static Map, Set> projectDependencies(Map, Set> originalDependencies, Set targetAttributes) { - // only those attributes are considered as left-hand-side candidates that occur at least once in dependencies - Set leftCandidates = new HashSet(); - for (Entry, Set> dependency : originalDependencies.entrySet()) { - if (!isTrivial(dependency.getKey(), dependency.getValue())) // only if non-trivial - leftCandidates.addAll(Sets.intersection(dependency.getKey(), targetAttributes)); - } - - // Compute an initial list of nontrivial projected dependencies - it does not have to be minimal yet - Map, Set> initialDependencies = new HashMap, Set>(); - for (Set leftSet : Sets.powerSet(leftCandidates)) { - Set rightSet = Sets.intersection(closureOf(leftSet, originalDependencies), targetAttributes); - if (!isTrivial(leftSet, rightSet)) { - initialDependencies.put(leftSet, rightSet); - } - } - // Don't forget to include constants! - Set constants = Sets.intersection(closureOf(Collections.emptySet(), originalDependencies), targetAttributes); - if (! constants.isEmpty()) { - initialDependencies.put(Collections.emptySet(), constants); - } - - // Omit those dependencies where the LHS has superfluous attributes - Map, Set> solidDependencies = new HashMap, Set>(); - for (Entry, Set> dependency : initialDependencies.entrySet()) { - Set leftSet = dependency.getKey(); - Set rightSet = dependency.getValue(); - boolean solid = true; - for (A skipped : leftSet) { // what if we skip one attribute from the left set? - Set singleton = Collections.singleton(skipped); - Set candidate = Sets.difference(leftSet, singleton); - Set rightCandidate = initialDependencies.get(candidate); - if (rightCandidate != null) { - if (Sets.union(rightCandidate, singleton).containsAll(rightSet)) { - solid = false; - break; - } - } - } - if (solid) { - solidDependencies.put(leftSet, rightSet); - } - } - - // TODO perform proper minimization, - // see e.g. page 45 in http://www.cs.ubc.ca/~hkhosrav/db/slides/03.design%20theory.pdf - - return Collections.unmodifiableMap(solidDependencies); - } - - /** - * Adds a given dependency to a mutable accumulator. - * @since 1.5 - */ - public static void includeDependency(Map, Set> accumulator, Set left, Set right) { - Set accumulatorRights = accumulator.computeIfAbsent(left, l -> new HashSet<>()); - accumulatorRights.addAll(right); - } - - /** - * Adds all given dependencies to a mutable accumulator. - * @since 1.5 - */ - public static void includeDependencies(Map, Set> accumulator, Map, Set> additionalDependencies) { - for (Entry, Set> entry : additionalDependencies.entrySet()) { - includeDependency(accumulator, entry.getKey(), entry.getValue()); - } - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java deleted file mode 100644 index b4f848a7..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java +++ /dev/null @@ -1,62 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.planning.helpers; - -import java.util.Optional; -import java.util.function.BiFunction; - -import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.Accuracy; - -/** - * Helpers dealing with optionally present statistics information - * - * @author Gabor Bergmann - * @since 2.1 - * - */ -public class StatisticsHelper { - - private StatisticsHelper() { - // Hidden utility class constructor - } - - public static Optional estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy, - BiFunction> estimateCardinality) - { - if (groupMask.isIdentity()) return Optional.of(1.0); - - Accuracy numeratorAccuracy = requiredAccuracy; - Accuracy denominatorAccuracy = requiredAccuracy.reciprocal(); - TupleMask identityMask = TupleMask.identity(groupMask.sourceWidth); - - Optional totalCountEstimate = estimateCardinality.apply(identityMask, numeratorAccuracy); - Optional bucketCountEstimate = estimateCardinality.apply(groupMask, denominatorAccuracy); - - return totalCountEstimate.flatMap(matchCount -> - bucketCountEstimate.map(bucketCount -> - bucketCount == 0L ? 0L : ((double) matchCount) / bucketCount - )); - } - - public static Optional min(Optional a, Optional b) { - if (b.isPresent()) { - if (a.isPresent()) { - return Optional.of(Math.min(a.get(), b.get())); - } else return b; - } else return a; - } - public static Optional min(Optional a, double b) { - if (a.isPresent()) { - return Optional.of(Math.min(a.get(), b)); - } else return Optional.of(b); - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java deleted file mode 100644 index 926a591f..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java +++ /dev/null @@ -1,217 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.planning.helpers; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Queue; -import java.util.Set; -import java.util.stream.Collectors; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; - -/** - * @author Gabor Bergmann - * @author Tamas Szabo - */ -public class TypeHelper { - - private TypeHelper() { - // Hiding constructor for utility class - } - - /** - * Collects the type constraints for the specified collection of variables. The type constraints consist of the - * constraints directly enforced on the variable itself, plus all of those that the given variable is unified with - * through equalities. - * - * @param variables - * the variables in question - * @param constraints - * the constraints in the pattern body - * @param context - * the query meta context - * @return the mapping from variable to set of type constraints - * @since 1.6 - */ - public static Map> inferUnaryTypesFor(Iterable variables, - Set constraints, IQueryMetaContext context) { - Map> typeMap = TypeHelper.inferUnaryTypes(constraints, context); - return inferUnaryTypesFor(variables, typeMap); - } - - /** - * Collects the type constraints for the specified collection of variables. The type constraints consist of the - * constraints directly enforced on the variable itself, plus all of those that the given variable is unified with - * through equalities. - * - * The method accepts a type map which is the result of the basic type inference from the - * {@link TypeHelper.inferUnaryTypes} method. The purpose of this method is that the type map can be reused across - * several calls to this method. - * - * @param variables - * the variables in question - * @param typeMap - * the type map of inference results - * @return the mapping from variable to set of type constraints - * @since 1.6 - */ - public static Map> inferUnaryTypesFor(Iterable variables, - Map> typeMap) { - Map> result = new HashMap>(); - - for (PVariable original : variables) { - // it can happen that the variable was unified into an other one due to equalities - Set keys = new HashSet(); - PVariable current = original; - - while (current != null) { - Set judgements = typeMap.get(current); - if (judgements != null) { - for (TypeJudgement judgement : judgements) { - keys.add(judgement.getInputKey()); - } - } - current = current.getDirectUnifiedInto(); - } - - result.put(original, keys); - } - - return result; - } - - /** - * Infers unary type information for variables, based on the given constraints. - * - * Subsumptions are not taken into account. - * - * @param constraints - * the set of constraints to extract type info from - */ - public static Map> inferUnaryTypes(Set constraints, - IQueryMetaContext context) { - Set equivalentJudgements = getDirectJudgements(constraints, context); - Set impliedJudgements = typeClosure(equivalentJudgements, context); - - Map> results = new HashMap>(); - for (TypeJudgement typeJudgement : impliedJudgements) { - final IInputKey inputKey = typeJudgement.getInputKey(); - if (inputKey.getArity() == 1) { - PVariable variable = (PVariable) typeJudgement.getVariablesTuple().get(0); - Set inferredTypes = results.computeIfAbsent(variable, v -> new HashSet<>()); - inferredTypes.add(typeJudgement); - } - } - return results; - } - - /** - * Gets direct judgements reported by constraints. No closure is applied yet. - */ - public static Set getDirectJudgements(Set constraints, IQueryMetaContext context) { - Set equivalentJudgements = new HashSet(); - for (PConstraint pConstraint : constraints) { - if (pConstraint instanceof ITypeInfoProviderConstraint) { - equivalentJudgements.addAll(((ITypeInfoProviderConstraint) pConstraint).getImpliedJudgements(context)); - } - } - return equivalentJudgements; - } - - /** - * Calculates the closure of a set of type judgements, with respect to supertyping. - * - * @return the set of all type judgements in typesToClose and all their direct and indirect supertypes - */ - public static Set typeClosure(Set typesToClose, IQueryMetaContext context) { - return typeClosure(Collections. emptySet(), typesToClose, context); - } - - /** - * Calculates the closure of a set of type judgements (with respect to supertyping), where the closure has been - * calculated before for a given base set, but not for a separate delta set. - *

- * Precondition: the set (typesToClose MINUS delta) is already closed w.r.t. supertyping. - * - * @return the set of all type judgements in typesToClose and all their direct and indirect supertypes - * @since 1.6 - */ - public static Set typeClosure(Set preclosedBaseSet, Set delta, - IQueryMetaContext context) { - Queue queue = delta.stream().filter(input -> !preclosedBaseSet.contains(input)).collect(Collectors.toCollection(LinkedList::new)); - if (queue.isEmpty()) - return preclosedBaseSet; - - Set closure = new HashSet(preclosedBaseSet); - - Map> conditionalImplications = new HashMap<>(); - for (TypeJudgement typeJudgement : closure) { - conditionalImplications.putAll(typeJudgement.getConditionalImpliedJudgements(context)); - } - - do { - TypeJudgement deltaType = queue.poll(); - if (closure.add(deltaType)) { - // direct implications - queue.addAll(deltaType.getDirectlyImpliedJudgements(context)); - - // conditional implications, source key processed before, this is the condition key - final Set implicationSet = conditionalImplications.get(deltaType); - if (implicationSet != null) { - queue.addAll(implicationSet); - } - - // conditional implications, this is the source key - Map> deltaConditionalImplications = deltaType - .getConditionalImpliedJudgements(context); - for (Entry> entry : deltaConditionalImplications.entrySet()) { - if (closure.contains(entry.getKey())) { - // condition processed before - queue.addAll(entry.getValue()); - } else { - // condition not processed yet - conditionalImplications.computeIfAbsent(entry.getKey(), key -> new HashSet<>()) - .addAll(entry.getValue()); - } - } - } - } while (!queue.isEmpty()); - - return closure; - } - - /** - * Calculates a remainder set of types from a larger set, that are not subsumed by a given set of subsuming types. - * - * @param subsumableTypes - * a set of types from which some may be implied by the subsuming types - * @param subsumingTypes - * a set of types that may imply some of the subsuming types - * @return the collection of types in subsumableTypes that are NOT identical to or supertypes of any type in - * subsumingTypes. - */ - public static Set subsumeTypes(Set subsumableTypes, Set subsumingTypes, - IQueryMetaContext context) { - Set closure = typeClosure(subsumingTypes, context); - Set subsumed = new HashSet(subsumableTypes); - subsumed.removeAll(closure); - return subsumed; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java deleted file mode 100644 index 2c285b54..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java +++ /dev/null @@ -1,94 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.planning.operations; - -import java.util.Collections; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.planning.SubPlan; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * Represents a constraint application on a single parent SubPlan. - *

Either a "selection" filter operation according to a deferred PConstraint (or transform in case of eval/aggregate), or - * alternatively a shorthand for PJoin + a PEnumerate on the right input for an enumerable PConstraint. - * - *

WARNING: if there are coinciding variables in the variable tuple of the enumerable constraint, - * it is the responsibility of the compiler to check them for equality. - * - * @author Bergmann Gabor - * - */ -public class PApply extends POperation { - - private PConstraint pConstraint; - - public PApply(PConstraint pConstraint) { - super(); - this.pConstraint = pConstraint; - } - public PConstraint getPConstraint() { - return pConstraint; - } - - @Override - public String getShortName() { - return String.format("APPLY_%s", pConstraint.toString()); - } - - @Override - public Set getDeltaConstraints() { - return Collections.singleton(pConstraint); - } - - @Override - public int numParentSubPlans() { - return 1; - } - - @Override - public void checkConsistency(SubPlan subPlan) { - super.checkConsistency(subPlan); - for (SubPlan parentPlan : subPlan.getParentPlans()) - Preconditions.checkArgument(!parentPlan.getAllEnforcedConstraints().contains(pConstraint), - "Double-checking constraint %s", pConstraint); - // TODO obtain context? - //if (pConstraint instanceof DeferredPConstraint) - // Preconditions.checkArgument(((DeferredPConstraint) pConstraint).isReadyAt(subPlan, context)) - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime - * result - + ((pConstraint == null) ? 0 : pConstraint - .hashCode()); - return result; - } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof PApply)) - return false; - PApply other = (PApply) obj; - if (pConstraint == null) { - if (other.pConstraint != null) - return false; - } else if (!pConstraint.equals(other.pConstraint)) - return false; - return true; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java deleted file mode 100644 index a975d50c..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.planning.operations; - -import java.util.Collections; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; - -/** - * Represents a base relation defined by the instance set of an enumerable PConstraint; there are no parent SubPlans. - * - *

WARNING: if there are coinciding variables in the variable tuple of the enumerable constraint, - * it is the responsibility of the compiler to check them for equality. - * @author Bergmann Gabor - * - */ -public class PEnumerate extends POperation { - - EnumerablePConstraint enumerablePConstraint; - - public PEnumerate(EnumerablePConstraint enumerablePConstraint) { - super(); - this.enumerablePConstraint = enumerablePConstraint; - } - public EnumerablePConstraint getEnumerablePConstraint() { - return enumerablePConstraint; - } - - @Override - public Set getDeltaConstraints() { - return Collections.singleton(enumerablePConstraint); - } - @Override - public int numParentSubPlans() { - return 0; - } - @Override - public String getShortName() { - return enumerablePConstraint.toString(); - } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime - * result - + ((enumerablePConstraint == null) ? 0 : enumerablePConstraint - .hashCode()); - return result; - } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof PEnumerate)) - return false; - PEnumerate other = (PEnumerate) obj; - if (enumerablePConstraint == null) { - if (other.enumerablePConstraint != null) - return false; - } else if (!enumerablePConstraint.equals(other.enumerablePConstraint)) - return false; - return true; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java deleted file mode 100644 index 10e0a85a..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.planning.operations; - -import java.util.Collections; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; - -/** - * Represents a natural join of two parent SubPlans. - * @author Bergmann Gabor - * - */ -public class PJoin extends POperation { - -// // TODO leave here? is this a problem in equivalnece checking? -// private Set onVariables; - - public PJoin(/*Set onVariables*/) { - super(); - //this.onVariables = new HashSet(onVariables); - } -// public Set getOnVariables() { -// return onVariables; -// } - - @Override - public Set getDeltaConstraints() { - return Collections.emptySet(); - } - @Override - public int numParentSubPlans() { - return 2; - } - - @Override - public String getShortName() { - return "JOIN"; //String.format("JOIN_{%s}", Joiner.on(",").join(onVariables)); - } - - @Override - public int hashCode() { - return getClass().hashCode(); - } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof PJoin)) - return false; - return true; - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java deleted file mode 100644 index e71cf217..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.planning.operations; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.planning.SubPlan; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * Abstract superclass for representing a high-level query evaluation operation. - * - *

Subclasses correspond to various POperations modeled after relational algebra. - * - * @author Bergmann Gabor - * - */ -public abstract class POperation { - - /** - * Newly enforced constraints - */ - public abstract Set getDeltaConstraints(); - - public abstract String getShortName(); - - /** - * @return the number of SubPlans that must be specified as parents - */ - public abstract int numParentSubPlans(); - - /** - * Checks whether this constraint can be properly applied at the given SubPlan. - */ - public void checkConsistency(SubPlan subPlan) { - Preconditions.checkArgument(this == subPlan.getOperation(), "POperation misalignment"); - Preconditions.checkArgument(subPlan.getParentPlans().size() == numParentSubPlans(), "Incorrect number of parent SubPlans"); - } - - @Override - public String toString() { - return getShortName(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java deleted file mode 100644 index d0539b2c..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java +++ /dev/null @@ -1,109 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.planning.operations; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import tools.refinery.viatra.runtime.matchers.planning.SubPlan; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * Represents a projection of a single parent SubPlan onto a limited set of variables. - *

May optionally prescribe an ordering of variables (List, as opposed to Set). - * - * @author Bergmann Gabor - * - */ -public class PProject extends POperation { - - private Collection toVariables; - private boolean ordered; - - - public PProject(Set toVariables) { - super(); - this.toVariables = toVariables; - this.ordered = false; - } - public PProject(List toVariables) { - super(); - this.toVariables = toVariables; - this.ordered = true; - } - - public Collection getToVariables() { - return toVariables; - } - public boolean isOrdered() { - return ordered; - } - - @Override - public Set getDeltaConstraints() { - return Collections.emptySet(); - } - @Override - public int numParentSubPlans() { - return 1; - } - @Override - public void checkConsistency(SubPlan subPlan) { - super.checkConsistency(subPlan); - final SubPlan parentPlan = subPlan.getParentPlans().get(0); - - Preconditions.checkArgument(parentPlan.getVisibleVariables().containsAll(toVariables), - () -> toVariables.stream() - .filter(input -> !parentPlan.getVisibleVariables().contains(input)).map(PVariable::getName) - .collect(Collectors.joining(",", "Variables missing from project: ", ""))); - } - - @Override - public String getShortName() { - return String.format("PROJECT%s_{%s}", ordered? "!" : "", - toVariables.stream().map(PVariable::getName).collect(Collectors.joining(","))); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (ordered ? 1231 : 1237); - result = prime * result - + ((toVariables == null) ? 0 : toVariables.hashCode()); - return result; - } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof PProject)) - return false; - PProject other = (PProject) obj; - if (ordered != other.ordered) - return false; - if (toVariables == null) { - if (other.toVariables != null) - return false; - } else if (!toVariables.equals(other.toVariables)) - return false; - return true; - } - - - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java deleted file mode 100644 index 9e6ea10e..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.planning.operations; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; - -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; - -/** - * No constraints, and no parent SubPlan, just a (possibly empty) set of a priori known (input) variables. Satisfied by a single tuple. - * - *

Can also be used without a priori variables, - * e.g. as a "virtual parent" in extreme cases, - * such as pattern foo(Bar) = {Bar = eval (3*4)} - * - * @author Bergmann Gabor - * - */ -public class PStart extends POperation { - - private Set aPrioriVariables; - - - public PStart(Set aPrioriVariables) { - super(); - this.aPrioriVariables = aPrioriVariables; - } - public PStart(PVariable... aPrioriVariables) { - this(new HashSet(Arrays.asList(aPrioriVariables))); - } - public Set getAPrioriVariables() { - return aPrioriVariables; - } - - @Override - public String getShortName() { - return aPrioriVariables.stream().map(PVariable::getName).collect(Collectors.joining(",", "START_{", "}")); - } - @Override - public int numParentSubPlans() { - return 0; - } - - @Override - public Set getDeltaConstraints() { - return Collections.emptySet(); - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime - * result - + ((aPrioriVariables == null) ? 0 : aPrioriVariables.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof PStart)) - return false; - PStart other = (PStart) obj; - if (aPrioriVariables == null) { - if (other.aPrioriVariables != null) - return false; - } else if (!aPrioriVariables.equals(other.aPrioriVariables)) - return false; - return true; - } - - - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java deleted file mode 100644 index eda4aa25..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java +++ /dev/null @@ -1,108 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * @author Gabor Bergmann - * - */ -public abstract class BasePConstraint implements PConstraint { - - - protected PBody pBody; - private final Set affectedVariables; - - - private final int sequentialID = nextID.getAndIncrement(); - - // Use a static atomic integer to avoid race conditions when creating new constraints. - private static AtomicInteger nextID = new AtomicInteger(0); - - public BasePConstraint(PBody pBody, Set affectedVariables) { - super(); - this.pBody = pBody; - this.affectedVariables = new HashSet(affectedVariables); - - for (PVariable pVariable : affectedVariables) { - pVariable.refer(this); - } - pBody.registerConstraint(this); - } - - @Override - public String toString() { - return "PC[" + getClass().getSimpleName() + ":" + toStringRest() + "]"; - } - - protected abstract String toStringRest(); - - @Override - public Set getAffectedVariables() { - return affectedVariables; - } - - @Override - public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { - return Collections.emptyMap(); - } - - @Override - public void replaceVariable(PVariable obsolete, PVariable replacement) { - pBody.checkMutability(); - if (affectedVariables.remove(obsolete)) { - affectedVariables.add(replacement); - obsolete.unrefer(this); - replacement.refer(this); - doReplaceVariable(obsolete, replacement); - } - } - - protected abstract void doReplaceVariable(PVariable obsolete, PVariable replacement); - - @Override - public void delete() { - pBody.checkMutability(); - for (PVariable pVariable : affectedVariables) { - pVariable.unrefer(this); - } - pBody.unregisterConstraint(this); - } - - @Override - public void checkSanity() { - } - - /** - * For backwards compatibility. Equivalent to {@link #getBody()} - */ - public PBody getPSystem() { - return pBody; - } - /** - * @since 2.1 - */ - @Override - public PBody getBody() { - return pBody; - } - - @Override - public int getMonotonousID() { - return sequentialID; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java deleted file mode 100644 index d2bf088c..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.planning.SubPlan; - -/** - * Any constraint that can only be checked on certain SubPlans (e.g. those plans that already contain some variables). - * - * @author Gabor Bergmann - * - */ -public abstract class DeferredPConstraint extends BasePConstraint { - - public DeferredPConstraint(PBody pBody, Set affectedVariables) { - super(pBody, affectedVariables); - } - - public abstract boolean isReadyAt(SubPlan plan, IQueryMetaContext context); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java deleted file mode 100644 index 9129aa47..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * A constraint for which all satisfying tuples of variable values can be enumerated at any point during run-time. - * - * @author Gabor Bergmann - * - */ -public abstract class EnumerablePConstraint extends BasePConstraint { - protected Tuple variablesTuple; - - protected EnumerablePConstraint(PBody pBody, Tuple variablesTuple) { - super(pBody, variablesTuple. getDistinctElements()); - this.variablesTuple = variablesTuple; - } - - @Override - public void doReplaceVariable(PVariable obsolete, PVariable replacement) { - variablesTuple = variablesTuple.replaceAll(obsolete, replacement); - } - - @Override - protected String toStringRest() { - String stringRestRest = toStringRestRest(); - String tupleString = "@" + variablesTuple.toString(); - return stringRestRest == null ? tupleString : ":" + stringRestRest + tupleString; - } - - protected String toStringRestRest() { - return null; - } - - public Tuple getVariablesTuple() { - return variablesTuple; - } - - @Override - public Set getDeducedVariables() { - return getAffectedVariables(); - } - - public PVariable getVariableInTuple(int index) { - return (PVariable) this.variablesTuple.get(index); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java deleted file mode 100644 index 686999f7..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem; - -/** - * An expression evaluator is used to execute arbitrary Java code during pattern matching. In order to include the - * evaluation in the planning seemlessly it is expected from the evaluator implementors to report all used PVariables by - * name. - * - * @author Zoltan Ujhelyi - * - */ -public interface IExpressionEvaluator { - - /** - * A textual description of the expression. Used only for debug purposes, but must not be null. - */ - String getShortDescription(); - - /** - * All input parameter names should be reported correctly. - */ - Iterable getInputParameterNames(); - - /** - * The expression evaluator code - * - * @param provider - * the value provider is an engine-specific way of reading internal variable tuples to evaluate the - * expression with - * @return the result of the expression: in case of predicate evaluation the return value must be true or false; - * otherwise the result can be an arbitrary object. No null values should be returned. - * @throws Exception - */ - Object evaluateExpression(IValueProvider provider) throws Exception; -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java deleted file mode 100644 index 8f647c64..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2022, Tamas Szabo, GitHub - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.Collection; - -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * A {@link PConstraint} that implements this interface refers to a list of PQueries. - * - * @author Tamas Szabo - * @since 2.8 - * - */ -public interface IMultiQueryReference { - - Collection getReferredQueries(); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java deleted file mode 100644 index 9ee05b39..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.Collections; -import java.util.List; - -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * A {@link PConstraint} that implements this interface refers to a {@link PQuery}. - * - * @author Zoltan Ujhelyi - * - */ -public interface IQueryReference extends IMultiQueryReference { - - PQuery getReferredQuery(); - - /** - * @since 2.8 - */ - @Override - default List getReferredQueries() { - return Collections.singletonList(getReferredQuery()); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java deleted file mode 100644 index e4c396d8..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2022, Tamas Szabo, GitHub - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.List; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * Implementations of this interface take an arbitrary number of input relations with their contents and compute the - * tuples of a single output relation. - * - * @author Tamas Szabo - * @since 2.8 - * - */ -public interface IRelationEvaluator { - - /** - * A textual description of the evaluator. Used only for debug purposes, but must not be null. - */ - String getShortDescription(); - - /** - * The relation evaluator code. For performance reasons, it is expected that the returned set is a mutable - * collection, and the caller must be allowed to actually perform mutations! - */ - Set evaluateRelation(List> inputs) throws Exception; - - /** - * For each input relation that this evaluator requires, this method returns the expected arities of the relations in order. - */ - List getInputArities(); - - /** - * Returns the arity of the output relation that this evaluator computes. - */ - int getOutputArity(); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java deleted file mode 100644 index b72035a8..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -import java.util.Set; - -/** - * Common superinterface of enumerable and deferred type constraints. - * @author Bergmann Gabor - * - */ -public interface ITypeConstraint extends ITypeInfoProviderConstraint { - - public abstract TypeJudgement getEquivalentJudgement(); - - /** - * Static internal utility class for implementations of {@link ITypeConstraint}s. - * @author Bergmann Gabor - */ - public static class TypeConstraintUtil { - - private TypeConstraintUtil() { - // Hiding constructor for utility class - } - - public static Map, Set> getFunctionalDependencies(IQueryMetaContext context, IInputKey inputKey, Tuple variablesTuple) { - final Map, Set> result = new HashMap, Set>(); - - Set, Set>> dependencies = context.getFunctionalDependencies(inputKey).entrySet(); - for (Entry, Set> dependency : dependencies) { - result.put( - transcribeVariables(dependency.getKey(), variablesTuple), - transcribeVariables(dependency.getValue(), variablesTuple) - ); - } - - return result; - } - - private static Set transcribeVariables(Set indices, Tuple variablesTuple) { - Set result = new HashSet(); - for (Integer index : indices) { - result.add((PVariable) variablesTuple.get(index)); - } - return result; - } - - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java deleted file mode 100644 index ff127d38..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java +++ /dev/null @@ -1,28 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; - -/** - * @author Gabor Bergmann - * - */ -public interface ITypeInfoProviderConstraint extends PConstraint { - - /** - * Returns type information implied by this constraint. - * - */ - public Set getImpliedJudgements(IQueryMetaContext context); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java deleted file mode 100644 index d959adc4..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem; - -/** - * Helper interface to get values from a tuple of variables. All pattern matching engines are expected to implement this - * to handle their internal structures. - * - * @author Zoltan Ujhelyi - * - */ -public interface IValueProvider { - - /** - * Returns the value of the selected variable - * @param variableName - * @return the value of the variable; never null - * @throws IllegalArgumentException if the variable is not defined - */ - Object getValue(String variableName); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java deleted file mode 100644 index a82d12ec..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; -import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PProblem; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * Adds extra methods to the PQuery interface to initialize its contents. - * - * @author Zoltan Ujhelyi - * - */ -public interface InitializablePQuery extends PQuery { - - /** - * Sets the query status. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED uninitialized}. - * - * @param status the new status - */ - void setStatus(PQueryStatus status); - - /** - * Adds a detected error. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED uninitialized}. - * - * @param problem the new problem - */ - void addError(PProblem problem); - - /** - * Sets up the bodies of the pattern. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED - * uninitialized}. - * - * @param bodies - * @throws ViatraQueryRuntimeException - */ - void initializeBodies(Set bodies); - - /** - * Adds an annotation to the specification. Only applicable if the pattern is still - * {@link PQueryStatus#UNINITIALIZED uninitialized}. - * - * @param annotation - */ - void addAnnotation(PAnnotation annotation); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java deleted file mode 100644 index 91eea817..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem; - -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * @author Gabor Bergmann - * - */ -public abstract class KeyedEnumerablePConstraint extends EnumerablePConstraint { - - protected KeyType supplierKey; - - public KeyedEnumerablePConstraint(PBody pBody, Tuple variablesTuple, - KeyType supplierKey) { - super(pBody, variablesTuple); - this.supplierKey = supplierKey; - } - - @Override - protected String toStringRestRest() { - return supplierKey == null ? "$any(null)" : keyToString(); - } - - protected abstract String keyToString(); - - public KeyType getSupplierKey() { - return supplierKey; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java deleted file mode 100644 index c38dc23a..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java +++ /dev/null @@ -1,289 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; -import java.util.stream.Collectors; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * A set of constraints representing a pattern body - * - * @author Gabor Bergmann - * - */ -public class PBody implements PTraceable { - - public static final String VIRTUAL_VARIABLE_PREFIX = ".virtual"; - private static final String VIRTUAL_VARIABLE_PATTERN = VIRTUAL_VARIABLE_PREFIX + "{%d}"; - - private PQuery query; - - /** - * If null, then parent query status is reused - */ - private PQueryStatus status = PQueryStatus.UNINITIALIZED; - - private Set allVariables; - private Set uniqueVariables; - private List symbolicParameters; - private Map variablesByName; - private Set constraints; - private int nextVirtualNodeID; - private PDisjunction containerDisjunction; - - public PBody(PQuery query) { - super(); - this.query = query; - allVariables = new LinkedHashSet<>(); - uniqueVariables = new LinkedHashSet<>(); - variablesByName = new HashMap<>(); - constraints = new LinkedHashSet<>(); - } - - /** - * @return whether the submission of the new variable was successful - */ - private boolean addVariable(PVariable var) { - checkMutability(); - Object name = var.getName(); - if (!variablesByName.containsKey(name)) { - allVariables.add(var); - if (var.isUnique()) - uniqueVariables.add(var); - variablesByName.put(name, var); - return true; - } else { - return false; - } - } - - /** - * Use this method to add a newly created constraint to the pSystem. - * - * @return whether the submission of the new constraint was successful - */ - boolean registerConstraint(PConstraint constraint) { - checkMutability(); - return constraints.add(constraint); - } - - /** - * Use this method to remove an obsolete constraint from the pSystem. - * - * @return whether the removal of the constraint was successful - */ - boolean unregisterConstraint(PConstraint constraint) { - checkMutability(); - return constraints.remove(constraint); - } - - @SuppressWarnings("unchecked") - public Set getConstraintsOfType(Class constraintClass) { - Set result = new HashSet(); - for (PConstraint pConstraint : constraints) { - if (constraintClass.isInstance(pConstraint)) - result.add((ConstraintType) pConstraint); - } - return result; - } - - public PVariable newVirtualVariable() { - checkMutability(); - String name; - do { - - name = String.format(VIRTUAL_VARIABLE_PATTERN, nextVirtualNodeID++); - } while (variablesByName.containsKey(name)); - PVariable var = new PVariable(this, name, true); - addVariable(var); - return var; - } - - public PVariable newVirtualVariable(String name) { - checkMutability(); - Preconditions.checkArgument(!variablesByName.containsKey(name), "ID %s already used for a virtual variable", name); - PVariable var = new PVariable(this, name, true); - addVariable(var); - return var; - } - - public PVariable newConstantVariable(Object value) { - checkMutability(); - PVariable virtual = newVirtualVariable(); - new ConstantValue(this, virtual, value); - return virtual; - } - - public Set getAllVariables() { - return allVariables; - } - - public Set getUniqueVariables() { - return uniqueVariables; - } - - private PVariable getVariableByName(Object name) { - return variablesByName.get(name).getUnifiedIntoRoot(); - } - - /** - * Find a PVariable by name - * - * @param name - * @return the found variable - * @throws IllegalArgumentException - * if no PVariable is found with the selected name - */ - public PVariable getVariableByNameChecked(Object name) { - if (!variablesByName.containsKey(name)) - throw new IllegalArgumentException(String.format("Cannot find PVariable %s", name)); - return getVariableByName(name); - } - - /** - * Finds and returns a PVariable by name. If no PVariable exists with the name in the body, a new one is created. If - * the name of the variable starts with {@value #VIRTUAL_VARIABLE_PREFIX}, the created variable will be considered - * virtual. - * - * @param name - * @return a PVariable with the selected name; never null - */ - public PVariable getOrCreateVariableByName(String name) { - checkMutability(); - if (!variablesByName.containsKey(name)) { - addVariable(new PVariable(this, name, name.startsWith(VIRTUAL_VARIABLE_PREFIX))); - } - return getVariableByName(name); - } - - public Set getConstraints() { - return constraints; - } - - public PQuery getPattern() { - return query; - } - - void noLongerUnique(PVariable pVariable) { - assert (!pVariable.isUnique()); - uniqueVariables.remove(pVariable); - } - - /** - * Returns the symbolic parameters of the body.

- * - *

- * Warning: if two PVariables are unified, the returned list changes. If you want to have a stable - * version, consider using {@link #getSymbolicParameters()}. - * - * @return a non-null, but possibly empty list - */ - public List getSymbolicParameterVariables() { - return getSymbolicParameters().stream().map(ExportedParameter::getParameterVariable) - .collect(Collectors.toList()); - } - - /** - * Returns the exported parameter constraints of the body. - * - * @return a non-null, but possibly empty list - */ - public List getSymbolicParameters() { - if (symbolicParameters == null) - symbolicParameters = new ArrayList<>(); - return symbolicParameters; - } - - /** - * Sets the exported parameter constraints of the body, if this instance is mutable. - * @param symbolicParameters the new value - */ - public void setSymbolicParameters(List symbolicParameters) { - checkMutability(); - this.symbolicParameters = new ArrayList<>(symbolicParameters); - } - - /** - * Sets a specific status for the body. If set, the parent PQuery status will not be checked; if set to null, its corresponding PQuery - * status is checked for mutability. - * - * @param status - * the status to set - */ - public void setStatus(PQueryStatus status) { - this.status = status; - } - - public boolean isMutable() { - if (status == null) { - return query.isMutable(); - } else { - return status.equals(PQueryStatus.UNINITIALIZED); - } - } - - void checkMutability() { - if (status == null) { - query.checkMutability(); - } else { - Preconditions.checkState(status.equals(PQueryStatus.UNINITIALIZED), "Initialized queries are not mutable"); - } - } - - /** - * Returns the disjunction the body is contained with. This disjunction may either be the - * {@link PQuery#getDisjunctBodies() canonical disjunction of the corresponding query} or something equivalent. - * - * @return the container disjunction of the body. Can be null if body is not in a disjunction yet. - */ - public PDisjunction getContainerDisjunction() { - return containerDisjunction; - } - - /** - * @param containerDisjunction the containerDisjunction to set - */ - public void setContainerDisjunction(PDisjunction containerDisjunction) { - Preconditions.checkArgument(query.equals(containerDisjunction.getQuery()), "Disjunction of pattern %s incompatible with body %s", containerDisjunction.getQuery().getFullyQualifiedName(), query.getFullyQualifiedName()); - Preconditions.checkState(this.containerDisjunction == null, "Disjunction is already set."); - this.containerDisjunction = containerDisjunction; - } - - /** - * All unary input keys directly prescribed by constraints, grouped by variable. - *

to supertype inference or subsumption applied at this point. - */ - public Map> getAllUnaryTypeRestrictions(IQueryMetaContext context) { - Map> currentRestrictions = allUnaryTypeRestrictions.get(context); - if (currentRestrictions == null) { - currentRestrictions = TypeHelper.inferUnaryTypes(getConstraints(), context); - allUnaryTypeRestrictions.put(context, currentRestrictions); - } - return currentRestrictions; - } - private WeakHashMap>> allUnaryTypeRestrictions = new WeakHashMap>>(); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java deleted file mode 100644 index ae2c4632..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.Comparator; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; - -/** - * @author Gabor Bergmann - * - */ -public interface PConstraint extends PTraceable { - - /** - * @since 2.1 - * @return the query body this constraint belongs to - */ - public PBody getBody(); - - /** - * All variables affected by this constraint. - */ - public Set getAffectedVariables(); - - /** - * The set of variables whose potential values can be enumerated (once all non-deduced variables have known values). - */ - public Set getDeducedVariables(); - - /** - * A (preferably minimal) cover of known functional dependencies between variables. - * @noreference Use {@link QueryAnalyzer} instead to properly handle dependencies of pattern calls. - * @return non-trivial functional dependencies in the form of {variables} --> {variables}, where dependencies with the same lhs are unified. - */ - public Map,Set> getFunctionalDependencies(IQueryMetaContext context); - - public void replaceVariable(PVariable obsolete, PVariable replacement); - - public void delete(); - - public void checkSanity(); - - /** - * Returns an integer ID that is guaranteed to increase strictly monotonously for constraints within a pBody. - */ - public abstract int getMonotonousID(); - - - /** - * A comparator that orders constraints by their {@link #getMonotonousID() monotonous identifiers}. Should only used - * for tiebreaking in other comparators. - * - * @since 2.0 - */ - public static final Comparator COMPARE_BY_MONOTONOUS_ID = (arg0, arg1) -> arg0.getMonotonousID() - arg1.getMonotonousID(); - - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java deleted file mode 100644 index f0241a9c..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java +++ /dev/null @@ -1,16 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Dénes Harmath, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem; - -/** - * Marker interface for PSystem elements that can be traced. - * @since 1.6 - */ -public interface PTraceable { -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java deleted file mode 100644 index b6ea4861..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java +++ /dev/null @@ -1,203 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.HashSet; -import java.util.Set; - -/** - * @author Gabor Bergmann - * - */ -public class PVariable { - private PBody pBody; - /** - * The name of the pattern variable. This is the unique key of the pattern node. - */ - private String name; - /** - * virtual pVariables are nodes that do not correspond to actual pattern variables; they represent constants or Term - * substitutes - */ - private boolean virtual; - - /** - * Set of constraints that mention this variable - */ - private Set referringConstraints; - - /** - * Determines whether there are any constraints that can deduce this variable - */ - private Boolean deducable; - - /** - * Another PVariable this variable has been unified into. Please use the other variable instead of this. Null iff - * this is still a first-class variable. - */ - private PVariable unifiedInto; - - PVariable(PBody pBody, String name) { - this(pBody, name, false); - } - - PVariable(PBody pBody, String name, boolean virtual) { - super(); - this.pBody = pBody; - this.name = name; - this.virtual = virtual; - // this.exportedParameter = false; - this.referringConstraints = new HashSet(); - this.unifiedInto = null; - this.deducable = false; - } - - /** - * Replaces this variable with a given other, resulting in their unification. This variable will no longer be - * unique. - * - * @param replacement - */ - public void unifyInto(PVariable replacement) { - pBody.checkMutability(); - replacementCheck(); - replacement = replacement.getUnifiedIntoRoot(); - - if (this.equals(replacement)) - return; - - if (!this.isVirtual() && replacement.isVirtual()) { - replacement.unifyInto(this); - } else { - // replacement.referringConstraints.addAll(this.referringConstraints); - // replacement.exportedParameter |= this.exportedParameter; - replacement.virtual &= this.virtual; - if (replacement.deducable != null && this.deducable != null) - replacement.deducable |= this.deducable; - else - replacement.deducable = null; - Set snapshotConstraints = // avoid ConcurrentModificationX - new HashSet(this.referringConstraints); - for (PConstraint constraint : snapshotConstraints) { - constraint.replaceVariable(this, replacement); - } - // replacementCheck() will fail from this point - this.unifiedInto = replacement; - pBody.noLongerUnique(this); - } - } - - /** - * Determines whether there are any constraints that can deduce this variable - */ - public boolean isDeducable() { - replacementCheck(); - if (deducable == null) { - deducable = false; - for (PConstraint pConstraint : getReferringConstraints()) { - if (pConstraint.getDeducedVariables().contains(this)) { - deducable = true; - break; - } - } - } - return deducable; - } - - /** - * Register that this variable is referred by the given constraint. - * - * @param constraint - */ - public void refer(PConstraint constraint) { - pBody.checkMutability(); - replacementCheck(); - deducable = null; - referringConstraints.add(constraint); - } - - /** - * Register that this variable is no longer referred by the given constraint. - * - * @param constraint - */ - public void unrefer(PConstraint constraint) { - pBody.checkMutability(); - replacementCheck(); - deducable = null; - referringConstraints.remove(constraint); - } - - /** - * @return the name of the pattern variable. This is the unique key of the pattern node. - */ - public String getName() { - replacementCheck(); - return name; - } - - /** - * @return the virtual - */ - public boolean isVirtual() { - replacementCheck(); - return virtual; - } - - /** - * @return the referringConstraints - */ - public Set getReferringConstraints() { - replacementCheck(); - return referringConstraints; - } - - @SuppressWarnings("unchecked") - public Set getReferringConstraintsOfType(Class constraintClass) { - replacementCheck(); - Set result = new HashSet(); - for (PConstraint pConstraint : referringConstraints) { - if (constraintClass.isInstance(pConstraint)) - result.add((ConstraintType) pConstraint); - } - return result; - } - - @Override - public String toString() { - // replacementCheck(); - return name;// + ":PatternNode"; - } - - public PVariable getDirectUnifiedInto() { - return unifiedInto; - } - - public PVariable getUnifiedIntoRoot() { - PVariable nextUnified = unifiedInto; - PVariable oldUnifiedInto = this; - while (nextUnified != null) { - oldUnifiedInto = nextUnified; - nextUnified = oldUnifiedInto.getDirectUnifiedInto(); - } - return oldUnifiedInto; // unifiedInto; - } - - public boolean isUnique() { - return unifiedInto == null; - } - - private void replacementCheck() { - if (unifiedInto != null) - throw new IllegalStateException("Illegal usage of variable " + name + " which has been replaced with " - + unifiedInto.name); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java deleted file mode 100644 index 4447b225..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java +++ /dev/null @@ -1,153 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.stream.Collectors; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.context.InputKeyImplication; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; - -/** - * A judgement that means that the given tuple of variables will represent a tuple of values that is a member of the extensional relation identified by the given input key. - * @author Bergmann Gabor - * - */ -public class TypeJudgement { - - private IInputKey inputKey; - private Tuple variablesTuple; - /** - * @param inputKey - * @param variablesTuple - */ - public TypeJudgement(IInputKey inputKey, Tuple variablesTuple) { - super(); - this.inputKey = inputKey; - this.variablesTuple = variablesTuple; - } - public IInputKey getInputKey() { - return inputKey; - } - public Tuple getVariablesTuple() { - return variablesTuple; - } - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result - + ((inputKey == null) ? 0 : inputKey.hashCode()); - result = prime * result - + ((variablesTuple == null) ? 0 : variablesTuple.hashCode()); - return result; - } - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof TypeJudgement)) - return false; - TypeJudgement other = (TypeJudgement) obj; - if (inputKey == null) { - if (other.inputKey != null) - return false; - } else if (!inputKey.equals(other.inputKey)) - return false; - if (variablesTuple == null) { - if (other.variablesTuple != null) - return false; - } else if (!variablesTuple.equals(other.variablesTuple)) - return false; - return true; - } - - public Set getDirectlyImpliedJudgements(IQueryMetaContext context) { - Set results = new HashSet(); - results.add(this); - - Collection implications = context.getImplications(this.inputKey); - for (InputKeyImplication inputKeyImplication : implications) { - results.add( - transcribeImplication(inputKeyImplication) - ); - } - - return results; - } - - /** - * @since 1.6 - */ - public Set getWeakenedAlternativeJudgements(IQueryMetaContext context) { - Set results = new HashSet(); - - Collection implications = context.getWeakenedAlternatives(this.inputKey); - for (InputKeyImplication inputKeyImplication : implications) { - results.add( - transcribeImplication(inputKeyImplication) - ); - } - - return results; - } - - /** - * @since 2.0 - */ - public Map> getConditionalImpliedJudgements(IQueryMetaContext context) { - return context.getConditionalImplications(this.inputKey).entrySet().stream().collect(Collectors.toMap( - entry -> transcribeImplication(entry.getKey()), - entry -> entry.getValue().stream().map(this::transcribeImplication).collect(Collectors.toSet()))); - } - - - - private TypeJudgement transcribeImplication(InputKeyImplication inputKeyImplication) { - return new TypeJudgement( - inputKeyImplication.getImpliedKey(), - transcribeVariablesToTuple(inputKeyImplication.getImpliedIndices()) - ); - } - private Tuple transcribeVariablesToTuple(List indices) { - Object[] elements = new Object[indices.size()]; - for (int i = 0; i < indices.size(); ++i) - elements[i] = variablesTuple.get(indices.get(i)); - return Tuples.flatTupleOf(elements); - } - - @Override - public String toString() { - return "TypeJudgement:" + inputKey.getPrettyPrintableName() + "@" + variablesTuple.toString(); - } - - /** - * Creates this judgement as a direct type constraint in the given PBody under construction. - *

pre: the variables tuple must be formed of variables of that PBody. - * @since 1.6 - */ - public PConstraint createConstraintFor(PBody pBody) { - if (inputKey.isEnumerable()) { - return new TypeConstraint(pBody, variablesTuple, inputKey); - } else { - return new TypeFilterConstraint(pBody, variablesTuple, inputKey); - } - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java deleted file mode 100644 index 8ea6bb93..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.planning.SubPlan; - -/** - * A kind of deferred constraint that can only be checked when a set of deferring variables are all present in a plan. - * - * @author Gabor Bergmann - * - */ -public abstract class VariableDeferredPConstraint extends DeferredPConstraint { - - public VariableDeferredPConstraint(PBody pBody, - Set affectedVariables) { - super(pBody, affectedVariables); - } - - public abstract Set getDeferringVariables(); - - /** - * Refine further if needed - */ - @Override - public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { - return plan.getVisibleVariables().containsAll(getDeferringVariables()); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java deleted file mode 100644 index 63a37bbe..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.aggregations; - -/** - * - * An aggregation operator that does not store interim results beyond the final aggregated value. - * @author Gabor Bergmann - * @since 1.4 - */ -public abstract class AbstractMemorylessAggregationOperator - implements IMultisetAggregationOperator -{ - - @Override - public AggregateResult getAggregate(AggregateResult result) { - return result; - } - - @Override - public AggregateResult clone(AggregateResult original) { - return original; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java deleted file mode 100644 index 4cc40a2b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.aggregations; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Inherited; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - -import tools.refinery.viatra.runtime.matchers.aggregators.count; - -/** - * The aggregator type annotation describes the type constraints for the selected aggregator. In version 1.4, two kinds of - * aggregators are supported: - * - *

    - *
  1. An aggregator that does not consider any parameter value from the call ({@link count}), just calculates the - * number of matches. This is represented by a single {@link Void} and a single corresponding return type.
  2. - *
  3. An aggregator that considers a single parameter from the call, and executes some aggregate operations over it. - * Such an aggregate operation can be defined over multiple types, where each possible parameter type has a corresponding return type declared.
  4. - *
- * - * Important! The parameterTypes and returnTypes arrays must have - *
    - *
  • The same number of classes defined each.
  • - *
  • Items are corresponded by index.
  • - *
  • Items should represent data types
  • - *
- * - * @author Zoltan Ujhelyi - * @since 1.4 - * - */ -@Target({ ElementType.TYPE }) -@Retention(RetentionPolicy.RUNTIME) -@Inherited -public @interface AggregatorType { - - Class[] parameterTypes(); - - Class[] returnTypes(); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java deleted file mode 100644 index e6972544..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.aggregations; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey; - -/** - * Augments an aggregator operator with type bindings for the type of values being aggregated and the aggregate result. - *

In case of count, the operator should be null. - * @author Gabor Bergmann - * @since 1.4 - */ -public class BoundAggregator { - private final IMultisetAggregationOperator operator; - private final Class domainType; - private final Class aggregateResultType; - - public BoundAggregator(IMultisetAggregationOperator operator, - Class domainType, - Class aggregateResultType) { - super(); - this.operator = operator; - this.domainType = domainType; - this.aggregateResultType = aggregateResultType; - } - - public IMultisetAggregationOperator getOperator() { - return operator; - } - - public Class getDomainType() { - return domainType; - } - - public Class getAggregateResultType() { - return aggregateResultType; - } - - public IInputKey getDomainTypeAsInputKey() { - return toJavaInputKey(domainType); - } - - public IInputKey getAggregateResultTypeAsInputKey() { - return toJavaInputKey(aggregateResultType); - } - - private static IInputKey toJavaInputKey(Class type) { - if (type==null) { - return null; - } else { - return new JavaTransitiveInstancesKey(type); - } - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java deleted file mode 100644 index c970bd6a..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Zoltan Ujhelyi, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.aggregations; - -/** - * - * Describes an aggregation operator keyword, potentially with type polymorphism. The actual runtime - * {@link IMultisetAggregationOperator} that implements the aggregation logic may depend on the type context. - * - *

- * Implementors are suggested to use lower-case classnames (as it will end up in the language) and are required use the - * annotation {@link AggregatorType} to indicate type inference rules. - * - *

- * Important! Implemented aggregators must be (1) deterministic (2) pure and (3)support incremental - * value updates in the internal operation. - * - * @author Zoltan Ujhelyi - * @since 1.4 - */ - -public interface IAggregatorFactory { - - /** - * Given type parameters selected from {@link AggregatorType} annotations, returns a run-time aggregator operator - * that is bound to the actual types. - * - * @param domainClass - * Java type of the values that are being aggregated - * @return the actual run-time aggregator logic, with type bindings - */ - public BoundAggregator getAggregatorLogic(Class domainClass); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java deleted file mode 100644 index 3bc22274..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java +++ /dev/null @@ -1,106 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.aggregations; - -import java.util.Collection; -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.aggregators.ExtremumOperator; - -/** - * A single column aggregator is used to incrementally compute the aggregate of a multiset of values according to an aggregator operator. - * - *

The operator provides two methods of computation:

    - *
  • Stateless aggregation of an explicit multiset, provided by {@link #aggregateStatelessly(Collection)}.
  • - *
  • Incremental aggregation, provided by {@link #createNeutral()}, {@link #update(Object, Object, boolean)}, {@link #isNeutral(Object)}, {@link #getAggregate(Object)}. - *
- * - *

In case of incremental computation, the aggregable multiset is conceptual; it is not represented by an explicit Collection object, but its update operations are tracked. - * - *

In case of incremental computation, internal results, potentially distinct from the final aggregate result, may be stored in a helper data structure called accumulator. - * The goal of this distinction is that the final result may not be sufficient for incremental updates (see e.g. {@link ExtremumOperator}). - * - * @author Gabor Bergmann - * - * @param the type of elements to be aggregated. - * @param the type used to store the interim results of the aggregate computation, - * that may be incrementally refreshed upon updates to the multiset, and that can easily yield the final result. - * @param the type of the final result of the aggregation to be output. - * - * @since 1.4 - */ -public interface IMultisetAggregationOperator { - - /** - * A textual description of the operator. - */ - String getShortDescription(); - - /** - * A name or identifier of the operator. - */ - String getName(); - - /** - * @return the neutral element, i.e. the interim result of aggregating an empty multiset. - */ - Accumulator createNeutral(); - - /** - * @return true if the interim result is equivalent to the neutral element, as if there are no values in the multiset. - * Must return true if the multiset is empty. - */ - boolean isNeutral(Accumulator result); - - /** - * @return an updated intermediate result, - * changed to reflect that a given object was added to / removed from the multiset - * (as indicated by the parameter isInsertion) - */ - Accumulator update(Accumulator oldResult, Domain updateValue, boolean isInsertion); - - /** - * @return the aggregate result obtained from the given intermediate result. - * May be null to indicate that the current multiset cannot be aggregated (e.g. 0 elements have no minimum). - */ - AggregateResult getAggregate(Accumulator result); - - /** - * Calculates the aggregate results from a given stream of values; all values are considered as inserted - * @return the aggregate result, or null if no result can be calculated (e.g. because of an empty stream) - * @since 2.0 - */ - AggregateResult aggregateStream(Stream stream); - - /** - * Clones the given accumulator (with all its internal contents). - */ - default Accumulator clone(Accumulator original) { - throw new UnsupportedOperationException(); - } - - /** - * Combines the given aggregate result and accumulator into a single aggregate result. - */ - default AggregateResult combine(AggregateResult left, Accumulator right) { - throw new UnsupportedOperationException(); - } - - default boolean contains(Domain value, Accumulator accumulator) { - throw new UnsupportedOperationException(); - } - - /** - * Pretty prints the contents of the given accumulator. - */ - default String prettyPrint(final Accumulator accumulator) { - return accumulator.toString(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java deleted file mode 100644 index e3f28cff..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java +++ /dev/null @@ -1,194 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.analysis; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.stream.Collectors; - -import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; -import tools.refinery.viatra.runtime.matchers.psystem.annotations.ParameterReference; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * Object responsible for computing and caching static query analysis results. - *

Any client can instantiate this to statically analyze queries. - * Query backends should share an instance obtained via {@link IQueryBackendContext} to save resources. - *

Precondition: all involved queries must be initialized. - * @noinstantiate Considered unstable API; subject to change in future versions. - * Either use the analyzer provided by {@link IQueryBackendContext}, or anticipate - * potential future breakage when instantiating your own analyzer. - * @author Gabor Bergmann - * @since 1.5 - */ -public final class QueryAnalyzer { - - private IQueryMetaContext metaContext; - - public QueryAnalyzer(IQueryMetaContext metaContext) { - this.metaContext = metaContext; - } - - // Functional dependencies - - /** - * Maps query and strictness to functional dependencies - */ - private Map, Set>> strictFunctionalDependencyGuarantees = - new HashMap<>(); - private Map, Set>> softFunctionalDependencyGuarantees = - new HashMap<>(); - - /** - * Functional dependency information, expressed on query parameters, that the match set of the query is guaranteed to respect. - *

The type dependencies shall be expressed on the parameter index integers, NOT the {@link PParameter} object. - * @return a non-null map of functional dependencies on parameters that can be processed by {@link FunctionalDependencyHelper} - * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; - * if false, user-provided soft dependencies (@FunctionalDependency) are included as well, that are anticipated but not guaranteed by the storage mechanism; - * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance - * @since 1.5 - */ - public Map, Set> getProjectedFunctionalDependencies(PQuery query, boolean strict) { - Map, Set>> guaranteeStore = strict ? strictFunctionalDependencyGuarantees : softFunctionalDependencyGuarantees; - Map, Set> dependencies = guaranteeStore.get(query); - // Why not computeIfAbsent? See Bug 532507 - // Invoked method #computeFunctionalDependencies may trigger functional dependency computation for called queries; - // and may thus recurs back into #getProjectedFunctionalDependencies, causing a ConcurrentModificationException - // if the called query has not been previously analyzed. - // - // Note: if patterns are recursive, the empty accumulator will be found in the store - // (this yields a safe lower estimate and guarantees termination for #getProjectedFunctionalDependencies) - // But this case probably will not occur due to recursive queries having a disjunction at some point, - // and thus ignored by #computeFunctionalDependencies - if (dependencies == null) { - dependencies = new HashMap<>(); // accumulator - guaranteeStore.put(query, dependencies); - computeFunctionalDependencies(dependencies, query, strict); - } - return dependencies; - } - - private void computeFunctionalDependencies(Map, Set> accumulator, PQuery query, boolean strict) { - Set bodies = query.getDisjunctBodies().getBodies(); - if (bodies.size() == 1) { // no support for recursion or disjunction - - PBody body = bodies.iterator().next(); - - // collect parameter variables - Map parameters = body.getSymbolicParameters().stream() - .collect(Collectors.toMap(ExportedParameter::getParameterVariable, - param -> query.getParameters().indexOf(param.getPatternParameter()))); - - // collect all internal dependencies - Map, Set> internalDependencies = - getFunctionalDependencies(body.getConstraints(), strict); - - // project onto parameter variables - Map, Set> projectedDeps = - FunctionalDependencyHelper.projectDependencies(internalDependencies, parameters.keySet()); - - // translate into indices - for (Entry, Set> entry : projectedDeps.entrySet()) { - Set left = new HashSet(); - Set right = new HashSet(); - for (PVariable pVariable : entry.getKey()) { - left.add(parameters.get(pVariable)); - } - for (PVariable pVariable : entry.getValue()) { - right.add(parameters.get(pVariable)); - } - accumulator.put(left, right); - } - - } else { - // Disjunctive case, no dependencies are inferred - // TODO: we can still salvage the intersection of dependencies IF - // - all bodies have disjoint match sets - // - and we avoid recursion - } - - // add annotation-based soft dependencies (regardless of number of bodies) - if (!strict) { - outer: - for (PAnnotation annotation : query.getAnnotationsByName("FunctionalDependency")) { - Set lefts = new HashSet(); - Set rights = new HashSet(); - - for (Object object : annotation.getAllValues("forEach")) { - ParameterReference parameter = (ParameterReference) object; - Integer position = query.getPositionOfParameter(parameter.getName()); - if (position == null) continue outer; - lefts.add(position); - } - for (Object object : annotation.getAllValues("unique")) { - ParameterReference parameter = (ParameterReference) object; - Integer position = query.getPositionOfParameter(parameter.getName()); - if (position == null) continue outer; - rights.add(position); - } - - FunctionalDependencyHelper.includeDependency(accumulator, lefts, rights); - } - } - } - - /** - * Functional dependency information, expressed on PVariables within a body, that the selected constraints imply. - * @return a non-null map of functional dependencies on PVariables that can be processed by {@link FunctionalDependencyHelper} - * @param constraints the set of constraints whose consequences will be analyzed - * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; - * if false, user-provided soft dependencies (@FunctionalDependency) are included as well, that are anticipated but not guaranteed by the storage mechanism; - * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance - * @since 1.5 - */ - public Map, Set> getFunctionalDependencies(Set constraints, boolean strict) { - Map, Set> accumulator = new HashMap, Set>(); - for (PConstraint pConstraint : constraints){ - if (pConstraint instanceof PositivePatternCall) { - // use query analysis results instead - PositivePatternCall call = (PositivePatternCall) pConstraint; - PQuery query = call.getSupplierKey(); - Map, Set> paramDependencies = getProjectedFunctionalDependencies(query, strict); - for (Entry, Set> entry : paramDependencies.entrySet()) { - Set lefts = new HashSet(); - Set rights = new HashSet(); - - for (Integer index : entry.getKey()) { - lefts.add(call.getVariableInTuple(index)); - } - for (Integer index : entry.getValue()) { - rights.add(call.getVariableInTuple(index)); - } - - FunctionalDependencyHelper.includeDependency(accumulator, - lefts, rights); - } - } else { - // delegate to PConstraint - FunctionalDependencyHelper.includeDependencies(accumulator, - pConstraint.getFunctionalDependencies(metaContext)); - } - } - return accumulator; - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java deleted file mode 100644 index c4fbe0e9..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java +++ /dev/null @@ -1,94 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.annotations; - -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.function.BiConsumer; - -import org.eclipse.collections.api.multimap.MutableMultimap; -import org.eclipse.collections.impl.multimap.list.FastListMultimap; - -/** - * A container describing query annotations - * @author Zoltan Ujhelyi - * - */ -public class PAnnotation { - - private final String name; - private MutableMultimap attributes = FastListMultimap.newMultimap(); - - public PAnnotation(String name) { - this.name = name; - - } - - /** - * Adds an attribute to the annotation - * @param attributeName - * @param value - */ - public void addAttribute(String attributeName, Object value) { - attributes.put(attributeName, value); - } - - /** - * Return the name of the annotation - */ - public String getName() { - return name; - } - - /** - * Returns the value of the first occurrence of an attribute - * @param attributeName - * @return the attribute value, or null, if attribute is not available - * @since 2.0 - */ - public Optional getFirstValue(String attributeName) { - return getAllValues(attributeName).stream().findFirst(); - } - - /** - * Returns the value of the first occurrence of an attribute - * @param attributeName - * @return the attribute value, or null, if attribute is not available - * @since 2.0 - */ - public Optional getFirstValue(String attributeName, Class clazz) { - return getAllValues(attributeName).stream().filter(clazz::isInstance).map(clazz::cast).findFirst(); - } - - /** - * Returns all values of a selected attribute - * @param attributeName - * @return a non-null, but possibly empty list of attributes - */ - public List getAllValues(String attributeName) { - return attributes.get(attributeName).toList(); - } - - /** - * Executes a consumer over all attributes. A selected attribute name (key) can appear (and thus consumed) multiple times. - * @since 2.0 - */ - public void forEachValue(BiConsumer consumer) { - attributes.forEachKeyValue(consumer::accept); - } - - /** - * Returns a set of all attribute names used in this annotation - * @since 2.1 - */ - public Set getAllAttributeNames() { - return attributes.keySet().toSet(); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java deleted file mode 100644 index c67e9046..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.annotations; - -/** - * An annotation parameter referencing a query parameter by name. Does not check whether the parameter exists. - * - * @author Zoltan Ujhelyi - * - */ -public class ParameterReference { - - final String name; - - public ParameterReference(String name) { - super(); - this.name = name; - } - - public String getName() { - return name; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java deleted file mode 100644 index 56f86e89..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java +++ /dev/null @@ -1,98 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; -import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; - -/** - * The PSystem representation of an aggregation. - * - * @author Tamas Szabo - * @since 1.4 - */ -public class AggregatorConstraint extends PatternCallBasedDeferred implements ITypeInfoProviderConstraint { - - protected PVariable resultVariable; - private BoundAggregator aggregator; - protected int aggregatedColumn; - - public AggregatorConstraint(BoundAggregator aggregator, PBody pBody, Tuple actualParametersTuple, PQuery query, - PVariable resultVariable, int aggregatedColumn) { - super(pBody, actualParametersTuple, query, Collections.singleton(resultVariable)); - this.resultVariable = resultVariable; - this.aggregatedColumn = aggregatedColumn; - this.aggregator = aggregator; - } - - public int getAggregatedColumn() { - return this.aggregatedColumn; - } - - public BoundAggregator getAggregator() { - return this.aggregator; - } - - @Override - public Set getDeducedVariables() { - return Collections.singleton(resultVariable); - } - - @Override - public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { - final Map, Set> result = new HashMap, Set>(); - result.put(getDeferringVariables(), getDeducedVariables()); - return result; - } - - @Override - protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { - if (resultVariable.equals(obsolete)) - resultVariable = replacement; - } - - @Override - protected Set getCandidateQuantifiedVariables() { - return actualParametersTuple. getDistinctElements(); - } - - @Override - protected String toStringRest() { - return query.getFullyQualifiedName() + "@" + actualParametersTuple.toString() + "->" - + resultVariable.toString(); - } - - public PVariable getResultVariable() { - return resultVariable; - } - - @Override - public Set getImpliedJudgements(IQueryMetaContext context) { - Set result = new HashSet(); - IInputKey aggregateResultType = aggregator.getAggregateResultTypeAsInputKey(); - if (aggregateResultType != null) { - result.add(new TypeJudgement(aggregateResultType, Tuples.staticArityFlatTupleOf(resultVariable))); - } - return result; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java deleted file mode 100644 index 7bc949a8..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java +++ /dev/null @@ -1,99 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.Collections; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.planning.SubPlan; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; -import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; - -/** - * @author Gabor Bergmann - * - */ -public abstract class BaseTypeSafeConstraint extends - VariableDeferredPConstraint { - - protected Set inputVariables; - protected PVariable outputVariable; - - public PVariable getOutputVariable() { - return outputVariable; - } - - /** - * @param pBody - * @param inputVariables - * @param outputVariable null iff no output (check-only) - */ - public BaseTypeSafeConstraint(PBody pBody, - Set inputVariables, final PVariable outputVariable) { - super(pBody, - (outputVariable == null) ? - inputVariables : - Stream.concat(inputVariables.stream(), Stream.of(outputVariable)).collect(Collectors.toSet()) - ); - this.inputVariables = inputVariables; - this.outputVariable = outputVariable; - } - - @Override - public Set getDeducedVariables() { - if (outputVariable == null) - return Collections.emptySet(); - else - return Collections.singleton(outputVariable); - } - - @Override - public Set getDeferringVariables() { - return inputVariables; - } - - @Override - public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { - if (super.isReadyAt(plan, context)) { - return checkTypeSafety(plan, context) == null; - } - return false; - } - - /** - * Checks whether all type restrictions are already enforced on affected variables. - * - * @param plan - * @return a variable whose type safety is not enforced yet, or null if the plan is typesafe - */ - public PVariable checkTypeSafety(SubPlan plan, IQueryMetaContext context) { - Set impliedJudgements = plan.getAllImpliedTypeJudgements(context); - - for (PVariable pVariable : inputVariables) { - Set allTypeRestrictionsForVariable = pBody.getAllUnaryTypeRestrictions(context).get(pVariable); - if (allTypeRestrictionsForVariable != null && !impliedJudgements.containsAll(allTypeRestrictionsForVariable)) - return pVariable; - } - return null; - } - - @Override - protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { - if (inputVariables.remove(obsolete)) - inputVariables.add(replacement); - if (outputVariable == obsolete) - outputVariable = replacement; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java deleted file mode 100644 index b978b62c..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java +++ /dev/null @@ -1,96 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.planning.SubPlan; -import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; - -/** - * @author Gabor Bergmann - * - */ -public class Equality extends DeferredPConstraint { - - private PVariable who; - private PVariable withWhom; - - public Equality(PBody pBody, PVariable who, PVariable withWhom) { - super(pBody, buildSet(who, withWhom)); - this.who = who; - this.withWhom = withWhom; - } - - private static Set buildSet(PVariable who, PVariable withWhom) { - Set set = new HashSet(); - set.add(who); - set.add(withWhom); - return set; - } - - /** - * An equality is moot if it compares the a variable with itself. - * - * @return true, if the equality is moot - */ - public boolean isMoot() { - return who.equals(withWhom); - } - - @Override - public void doReplaceVariable(PVariable obsolete, PVariable replacement) { - if (obsolete.equals(who)) - who = replacement; - if (obsolete.equals(withWhom)) - withWhom = replacement; - } - - @Override - protected String toStringRest() { - return who.getName() + "=" + withWhom.getName(); - } - - public PVariable getWho() { - return who; - } - - public PVariable getWithWhom() { - return withWhom; - } - - @Override - public Set getDeducedVariables() { - return Collections.emptySet(); - } - - @Override - public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { - final Map, Set> result = new HashMap, Set>(); - result.put(Collections.singleton(who), Collections.singleton(withWhom)); - result.put(Collections.singleton(withWhom), Collections.singleton(who)); - return result; - } - - @Override - public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { - return plan.getVisibleVariables().contains(who) && plan.getVisibleVariables().contains(withWhom); - // will be replaced by || if copierNode is available; - // until then, LayoutHelper.unifyVariablesAlongEqualities(PSystem) is - // recommended. - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java deleted file mode 100644 index 80706792..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java +++ /dev/null @@ -1,108 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.Collections; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * @author Gabor Bergmann - * - */ -public class ExportedParameter extends VariableDeferredPConstraint { - PVariable parameterVariable; - final String parameterName; - final PParameter patternParameter; - - /** - * @since 1.4 - */ - public ExportedParameter(PBody pBody, PVariable parameterVariable, PParameter patternParameter) { - super(pBody, Collections.singleton(parameterVariable)); - this.parameterVariable = parameterVariable; - this.patternParameter = patternParameter; - parameterName = patternParameter.getName(); - } - - @Override - public void doReplaceVariable(PVariable obsolete, PVariable replacement) { - if (obsolete.equals(parameterVariable)) - parameterVariable = replacement; - } - - @Override - protected String toStringRest() { - Object varName = parameterVariable.getName(); - return parameterName.equals(varName) ? parameterName : parameterName + "(" + varName + ")"; - } - - @Override - public Set getDeducedVariables() { - return Collections.emptySet(); - } - - /** - * The name of the parameter; usually, it is expected that {@link #getParameterVariable()} is more useful, except - * maybe for debugging purposes. - * - * @return a non-null name of the parameter - */ - public String getParameterName() { - return parameterName; - } - - public PVariable getParameterVariable() { - return parameterVariable; - } - - /** - * @since 1.4 - */ - public PParameter getPatternParameter() { - if (patternParameter == null) { - PQuery query = pBody.getPattern(); - Integer index = query.getPositionOfParameter(parameterName); - if (index == null) { - throw new IllegalStateException(String.format("Pattern %s does not have a parameter named %s", - query.getFullyQualifiedName(), parameterName)); - } - return query.getParameters().get(index); - } else { - return patternParameter; - } - } - - @Override - public Set getDeferringVariables() { - return Collections.singleton(parameterVariable); - } - - @Override - public void checkSanity() { - super.checkSanity(); - if (!parameterVariable.isDeducable()) { - String[] args = { parameterName }; - String msg = "Impossible to match pattern: " - + "exported pattern variable {1} can not be determined based on the pattern constraints. " - + "HINT: certain constructs (e.g. negative patterns or check expressions) cannot output symbolic parameters."; - String shortMsg = "Could not deduce value of parameter"; - throw new QueryProcessingException(msg, args, shortMsg, null); - } - - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java deleted file mode 100644 index 06688c36..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java +++ /dev/null @@ -1,80 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; - -/** - * @author Zoltan Ujhelyi - * - */ -public class ExpressionEvaluation extends BaseTypeSafeConstraint { - - private IExpressionEvaluator evaluator; - private boolean isUnwinding; - - public ExpressionEvaluation(PBody pBody, IExpressionEvaluator evaluator, PVariable outputVariable) { - this(pBody, evaluator, outputVariable, false); - } - - /** - * @since 2.4 - */ - public ExpressionEvaluation(PBody pBody, IExpressionEvaluator evaluator, PVariable outputVariable, - boolean isUnwinding) { - super(pBody, getPVariablesOfExpression(pBody, evaluator), outputVariable); - this.evaluator = evaluator; - this.isUnwinding = isUnwinding; - } - - /** - * @since 2.4 - */ - public boolean isUnwinding() { - return isUnwinding; - } - - public IExpressionEvaluator getEvaluator() { - return evaluator; - } - - @Override - protected String toStringRest() { - return Tuples.flatTupleOf(new ArrayList(inputVariables).toArray()).toString() + "|=" - + evaluator.getShortDescription(); - } - - @Override - public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { - if (outputVariable == null) - return Collections.emptyMap(); - else - return Collections.singletonMap(inputVariables, Collections.singleton(outputVariable)); - } - - private static Set getPVariablesOfExpression(PBody pBody, IExpressionEvaluator evaluator) { - // use a linked set, so that the variables will come in the order of the parameters - Set result = new LinkedHashSet(); - for (String name : evaluator.getInputParameterNames()) { - PVariable variable = pBody.getOrCreateVariableByName(name); - result.add(variable); - } - return result; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java deleted file mode 100644 index 5cac33dc..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java +++ /dev/null @@ -1,151 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; - -/** - * @author Gabor Bergmann - * - */ -public class Inequality extends VariableDeferredPConstraint { - - private PVariable who; - private PVariable withWhom; - - /** - * The inequality constraint is weak if it can be ignored when who is the same as withWhom, or if any if them is - * undeducible. - */ - private boolean weak; - - public Inequality(PBody pBody, PVariable who, PVariable withWhom) { - this(pBody, who, withWhom, false); - } - - public Inequality(PBody pBody, PVariable who, PVariable withWhom, - boolean weak) { - super(pBody, new HashSet<>(Arrays.asList(who, withWhom) )); - this.who = who; - this.withWhom = withWhom; - this.weak = weak; - } - - // private Inequality( - // PSystem pSystem, - // PVariable subject, Set inequals) - // { - // super(pSystem, include(inequals, subject)); - // this.subject = subject; - // this.inequals = inequals; - // } - - // private static HashSet include(Set inequals, PVariable subject) { - // HashSet hashSet = new HashSet(inequals); - // hashSet.add(subject); - // return hashSet; - // } - - @Override - public Set getDeferringVariables() { - return getAffectedVariables(); - } - - // private static int[] mapIndices(Map variablesIndex, Set keys) { - // int[] result = new int[keys.size()]; - // int k = 0; - // for (PVariable key : keys) { - // result[k++] = variablesIndex.get(key); - // } - // return result; - // } - - // @Override - // public IFoldablePConstraint getIncorporator() { - // return incorporator; - // } - // - // @Override - // public void registerIncorporatationInto(IFoldablePConstraint incorporator) { - // this.incorporator = incorporator; - // } - // - // @Override - // public boolean incorporate(IFoldablePConstraint other) { - // if (other instanceof Inequality) { - // Inequality other2 = (Inequality) other; - // if (subject.equals(other2.subject)) { - // Set newInequals = new HashSet(inequals); - // newInequals.addAll(other2.inequals); - // return new Inequality(buildable, subject, newInequals); - // } - // } else return false; - // } - - @Override - protected String toStringRest() { - return who.toString() + (isWeak() ? "!=?" : "!=") + withWhom.toString(); - } - - @Override - public void doReplaceVariable(PVariable obsolete, PVariable replacement) { - if (obsolete.equals(who)) - who = replacement; - if (obsolete.equals(withWhom)) - withWhom = replacement; - } - - @Override - public Set getDeducedVariables() { - return Collections.emptySet(); - } - - /** - * The inequality constraint is weak if it can be ignored when who is the same as withWhom, or if any if them is - * undeducible. - * - * @return the weak - */ - public boolean isWeak() { - return weak; - } - - /** - * A weak inequality constraint is eliminable if who is the same as withWhom, or if any if them is undeducible. - */ - public boolean isEliminable() { - return isWeak() && (who.equals(withWhom) || !who.isDeducable() || !withWhom.isDeducable()); - } - - /** - * Eliminates a weak inequality constraint if it can be ignored when who is the same as withWhom, or if any if them - * is undeducible. - */ - public void eliminateWeak() { - if (isEliminable()) - delete(); - } - - public PVariable getWho() { - return who; - } - - public PVariable getWithWhom() { - return withWhom; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java deleted file mode 100644 index 87d9d9fc..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java +++ /dev/null @@ -1,52 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.Collections; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * @author Gabor Bergmann - * - */ -public class NegativePatternCall extends PatternCallBasedDeferred { - - public NegativePatternCall(PBody pBody, Tuple actualParametersTuple, PQuery query) { - super(pBody, actualParametersTuple, query); - } - - @Override - public Set getDeducedVariables() { - return Collections.emptySet(); - } - - /** - * @return all variables that may potentially be quantified they are not used anywhere else - */ - @Override - protected Set getCandidateQuantifiedVariables() { - return getAffectedVariables(); - } - - @Override - protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { - } - - @Override - protected String toStringRest() { - return "!" + query.getFullyQualifiedName() + "@" + actualParametersTuple.toString(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java deleted file mode 100644 index 93eeffec..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java +++ /dev/null @@ -1,118 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; -import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * @author Gabor Bergmann - * - */ -public abstract class PatternCallBasedDeferred extends VariableDeferredPConstraint implements IQueryReference { - - protected Tuple actualParametersTuple; - - protected abstract void doDoReplaceVariables(PVariable obsolete, PVariable replacement); - - protected abstract Set getCandidateQuantifiedVariables(); - - protected PQuery query; - private Set deferringVariables; - - public PatternCallBasedDeferred(PBody pBody, Tuple actualParametersTuple, - PQuery pattern, Set additionalAffectedVariables) { - super(pBody, union(actualParametersTuple. getDistinctElements(), additionalAffectedVariables)); - this.actualParametersTuple = actualParametersTuple; - this.query = pattern; - } - - public PatternCallBasedDeferred(PBody pBody, Tuple actualParametersTuple, - PQuery pattern) { - this(pBody, actualParametersTuple, pattern, Collections. emptySet()); - } - - private static Set union(Set a, Set b) { - Set result = new HashSet(); - result.addAll(a); - result.addAll(b); - return result; - } - - @Override - public Set getDeferringVariables() { - if (deferringVariables == null) { - deferringVariables = new HashSet(); - for (PVariable var : getCandidateQuantifiedVariables()) { - if (var.isDeducable()) - deferringVariables.add(var); - } - } - return deferringVariables; - } - - @Override - public void checkSanity() { - super.checkSanity(); - for (Object obj : this.actualParametersTuple.getDistinctElements()) { - PVariable var = (PVariable) obj; - if (!getDeferringVariables().contains(var)) { - // so this is a free variable of the NAC / aggregation? - for (PConstraint pConstraint : var.getReferringConstraints()) { - if (pConstraint != this - && !(pConstraint instanceof Equality && ((Equality) pConstraint).isMoot())) - throw new QueryProcessingException ( - "Variable {1} of constraint {2} is not a positively determined part of the pattern, yet it is also affected by {3}.", - new String[] { var.toString(), this.toString(), pConstraint.toString() }, - "Read-only variable can not be deduced", null); - } - } - } - - } - -// public SubPlan getSidePlan(IOperationCompiler compiler) throws QueryPlannerException { -// SubPlan sidePlan = compiler.patternCallPlan(actualParametersTuple, query); -// sidePlan = BuildHelper.enforceVariableCoincidences(compiler, sidePlan); -// return sidePlan; -// } - - @Override - protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { - if (deferringVariables != null) { - // FAIL instead of hopeless attempt to fix - // if (deferringVariables.remove(obsolete)) deferringVariables.add(replacement); - throw new IllegalStateException("Cannot replace variables on " + this - + " when deferring variables have already been identified."); - } - actualParametersTuple = actualParametersTuple.replaceAll(obsolete, replacement); - doDoReplaceVariables(obsolete, replacement); - } - - public Tuple getActualParametersTuple() { - return actualParametersTuple; - } - - @Override - public PQuery getReferredQuery() { - return query; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java deleted file mode 100644 index 0c40d91e..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * @author Gabor Bergmann - */ -public class PatternMatchCounter extends PatternCallBasedDeferred { - - private PVariable resultVariable; - - public PatternMatchCounter(PBody pBody, Tuple actualParametersTuple, - PQuery query, PVariable resultVariable) { - super(pBody, actualParametersTuple, query, Collections.singleton(resultVariable)); - this.resultVariable = resultVariable; - } - - @Override - public Set getDeducedVariables() { - return Collections.singleton(resultVariable); - } - - @Override - public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { - final Map, Set> result = new HashMap, Set>(); - result.put(getDeferringVariables(), getDeducedVariables()); - return result; - } - - @Override - protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { - if (resultVariable.equals(obsolete)) - resultVariable = replacement; - } - - @Override - protected Set getCandidateQuantifiedVariables() { - return actualParametersTuple. getDistinctElements(); - } - - - @Override - protected String toStringRest() { - return query.getFullyQualifiedName() + "@" + actualParametersTuple.toString() + "->" - + resultVariable.toString(); - } - - public PVariable getResultVariable() { - return resultVariable; - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java deleted file mode 100644 index 336a83fb..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2022, Tamas Szabo, GitHub - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.List; - -import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference; -import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * A constraint which prescribes the evaluation of custom Java logic that takes an arbitrary number of input relations - * and produces one output relation. Contrast this to {@link ExpressionEvaluation}, which produces a single output value - * given an input tuple. - * - * The assumption is that the relation evaluation logic is not incremental, that is, it can only perform from-scratch - * computation of the output relation given the complete input relations. To this end, the relation evaluator always - * receives the complete input relations with all their contents as input. However, the evaluator engine makes sure that - * the output of the relation evaluation is at least "seemingly" incremental. This means that the underlying computation - * network computes the delta on the output compared to the previous output and only propagates the delta further. - * - * @author Tamas Szabo - * - * @since 2.8 - * - */ -public class RelationEvaluation extends EnumerablePConstraint implements IMultiQueryReference { - - private final IRelationEvaluator evaluator; - private final List inputQueries; - - public RelationEvaluation(final PBody body, final Tuple variablesTuple, final List inputQueries, - final IRelationEvaluator evaluator) { - super(body, variablesTuple); - this.evaluator = evaluator; - this.inputQueries = inputQueries; - } - - public IRelationEvaluator getEvaluator() { - return this.evaluator; - } - - @Override - public List getReferredQueries() { - return this.inputQueries; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java deleted file mode 100644 index 8b6e29ef..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; - -import java.util.Collections; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; -import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * Represents a non-enumerable type constraint that asserts that values substituted for the given tuple of variables - * form a tuple that belongs to a (typically non-enumerable) extensional relation identified by an {@link IInputKey}. - * - *

The InputKey is typically not enumerable. If it is enumerable, use {@link TypeConstraint} instead, so that the PConstraint carries over the property of enumerability. - * - * @author Bergmann Gabor - * - */ -public class TypeFilterConstraint extends VariableDeferredPConstraint implements - ITypeConstraint { - - private Tuple variablesTuple; - private IInputKey inputKey; - - private TypeJudgement equivalentJudgement; - - - public TypeFilterConstraint(PBody pBody, Tuple variablesTuple, IInputKey inputKey) { - super(pBody, variablesTuple. getDistinctElements()); - this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); - - this.variablesTuple = variablesTuple; - this.inputKey = inputKey; - - if (variablesTuple.getSize() != inputKey.getArity()) - throw new IllegalArgumentException( - this.getClass().getSimpleName() + - " applied for variable tuple " + variablesTuple + " having wrong arity for input key " + - inputKey); - } - - - - public Tuple getVariablesTuple() { - return variablesTuple; - } - - public IInputKey getInputKey() { - return inputKey; - } - - @Override - public TypeJudgement getEquivalentJudgement() { - return equivalentJudgement; - } - - @Override - protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { - variablesTuple = variablesTuple.replaceAll(obsolete, replacement); - this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); - } - - @Override - public Set getImpliedJudgements(IQueryMetaContext context) { - return Collections.singleton(equivalentJudgement); - } - - @Override - public Set getDeducedVariables() { - return Collections.emptySet(); - } - - @Override - public Set getDeferringVariables() { - return getAffectedVariables(); - } - - @Override - protected String toStringRest() { - return inputKey.getPrettyPrintableName() + "@" + variablesTuple; - } - - @Override - public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { - return TypeConstraintUtil.getFunctionalDependencies(context, inputKey, variablesTuple); - } - - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java deleted file mode 100644 index 7bbf7118..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; -import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * @since 2.0 - */ -public abstract class AbstractTransitiveClosure extends KeyedEnumerablePConstraint implements IQueryReference, ITypeInfoProviderConstraint { - - public AbstractTransitiveClosure(PBody pBody, Tuple variablesTuple, PQuery supplierKey) { - super(pBody, variablesTuple, supplierKey); - } - - @Override - public PQuery getReferredQuery() { - return supplierKey; - } - - /** - * @since 1.3 - */ - @Override - public Set getImpliedJudgements(IQueryMetaContext context) { - return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple); - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java deleted file mode 100644 index e3dae240..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Zoltan Ujhelyi, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * For a binary base pattern over an enumerable universe type, computes the reflexive transitive closure (base)* - * - * @author Gabor Bergmann, Zoltan Ujhelyi - * @since 2.0 - */ -public class BinaryReflexiveTransitiveClosure extends AbstractTransitiveClosure { - - private final IInputKey universeType; - - public BinaryReflexiveTransitiveClosure(PBody pBody, Tuple variablesTuple, - PQuery pattern, IInputKey universeType) { - super(pBody, variablesTuple, pattern); - this.universeType = universeType; - } - - @Override - protected String keyToString() { - return supplierKey.getFullyQualifiedName() + "*"; - } - - /** - * Returns the type whose instances should be returned as 0-long paths. - * @since 2.0 - */ - public IInputKey getUniverseType() { - return universeType; - } - - @Override - public void checkSanity() { - if (!universeType.isEnumerable() || universeType.getArity() != 1) { - throw new QueryProcessingException( - String.format("Invalid universe type %s - it should be enumerable and must have an arity of 1.", - universeType.getPrettyPrintableName()), - pBody.getPattern()); - } - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java deleted file mode 100644 index 716d043b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; - -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * For a binary base pattern, computes the irreflexive transitive closure (base)+ - * - * @author Gabor Bergmann - * - */ -public class BinaryTransitiveClosure extends AbstractTransitiveClosure { - - public BinaryTransitiveClosure(PBody pBody, Tuple variablesTuple, - PQuery pattern) { - super(pBody, variablesTuple, pattern); - } - - @Override - protected String keyToString() { - return supplierKey.getFullyQualifiedName() + "+"; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java deleted file mode 100644 index 10da2e21..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java +++ /dev/null @@ -1,11 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; - -public enum Connectivity { - WEAK, - STRONG; -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java deleted file mode 100644 index 251146c8..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; - -/** - * @author Gabor Bergmann - * - */ -public class ConstantValue extends KeyedEnumerablePConstraint { - - private PVariable variable; - - public ConstantValue(PBody pBody, PVariable variable, Object value) { - super(pBody, Tuples.staticArityFlatTupleOf(variable), value); - this.variable = variable; - } - - @Override - protected String keyToString() { - return supplierKey.toString(); - } - - /** - * @since 1.7 - */ - public PVariable getVariable() { - return variable; - } - - @Override - public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { - final Map, Set> result = new HashMap, Set>(); - final Set emptySet = Collections.emptySet(); // a constant value is functionally determined by everything - result.put(emptySet, Collections.singleton(getVariableInTuple(0))); - return result; - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java deleted file mode 100644 index 25ab34b4..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java +++ /dev/null @@ -1,76 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; - -import java.util.HashSet; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; -import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; - -/** - * @author Gabor Bergmann - * - */ -public class PositivePatternCall extends KeyedEnumerablePConstraint implements IQueryReference, ITypeInfoProviderConstraint { - - public PositivePatternCall(PBody pBody, Tuple variablesTuple, - PQuery pattern) { - super(pBody, variablesTuple, pattern); - } - - @Override - protected String keyToString() { - return supplierKey.getFullyQualifiedName(); - } - - // Note: #getFunctionalDependencies is intentionally not implemented - use QueryAnalyzer instead! -// @Override -// public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { -// return super.getFunctionalDependencies(context); -// } - - @Override - public PQuery getReferredQuery() { - return supplierKey; - } - - @Override - public Set getImpliedJudgements(IQueryMetaContext context) { - return getTypesImpliedByCall(supplierKey, variablesTuple); - } - - /** - * @since 1.3 - */ - public static Set getTypesImpliedByCall(PQuery calledQuery, Tuple actualParametersTuple) { - Set result = new HashSet(); - for (TypeJudgement parameterJudgement : calledQuery.getTypeGuarantees()) { - IInputKey inputKey = parameterJudgement.getInputKey(); - Tuple judgementIndexTuple = parameterJudgement.getVariablesTuple(); - - Object[] judgementVariables = new Object[judgementIndexTuple.getSize()]; - for (int i=0; i - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; - -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.*; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -import java.util.Set; - -public class RepresentativeElectionConstraint extends KeyedEnumerablePConstraint - implements IQueryReference, ITypeInfoProviderConstraint { - private final Connectivity connectivity; - - public RepresentativeElectionConstraint(PBody pBody, Tuple variablesTuple, PQuery supplierKey, - Connectivity connectivity) { - super(pBody, variablesTuple, supplierKey); - this.connectivity = connectivity; - } - - public Connectivity getConnectivity() { - return connectivity; - } - - @Override - public PQuery getReferredQuery() { - return supplierKey; - } - - @Override - public Set getImpliedJudgements(IQueryMetaContext context) { - return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple); - } - - @Override - protected String keyToString() { - return supplierKey.getFullyQualifiedName() + "#" + connectivity + "#representative"; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java deleted file mode 100644 index 2ca54cc0..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java +++ /dev/null @@ -1,79 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; - -import java.util.Collections; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; - -/** - * Represents an enumerable type constraint that asserts that values substituted for the given tuple of variables - * form a tuple that belongs to an enumerable extensional relation identified by an {@link IInputKey}. - * - *

The InputKey must be enumerable! - * - * @author Zoltan Ujhelyi - * - */ -public class TypeConstraint extends KeyedEnumerablePConstraint implements ITypeConstraint { - - private TypeJudgement equivalentJudgement; - - public TypeConstraint(PBody pBody, Tuple variablesTuple, IInputKey inputKey) { - super(pBody, variablesTuple, inputKey); - this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); - - if (! inputKey.isEnumerable()) - throw new IllegalArgumentException( - this.getClass().getSimpleName() + - " applicable for enumerable input keys only; received instead " + - inputKey); - if (variablesTuple.getSize() != inputKey.getArity()) - throw new IllegalArgumentException( - this.getClass().getSimpleName() + - " applied for variable tuple " + variablesTuple + " having wrong arity for input key " + - inputKey); - } - - @Override - protected String keyToString() { - return supplierKey.getPrettyPrintableName(); - } - - @Override - public TypeJudgement getEquivalentJudgement() { - return equivalentJudgement; - } - - @Override - public Set getImpliedJudgements(IQueryMetaContext context) { - return Collections.singleton(equivalentJudgement); - //return equivalentJudgement.getDirectlyImpliedJudgements(context); - } - - @Override - public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { - return TypeConstraintUtil.getFunctionalDependencies(context, supplierKey, variablesTuple); - } - - @Override - public void doReplaceVariable(PVariable obsolete, PVariable replacement) { - super.doReplaceVariable(obsolete, replacement); - this.equivalentJudgement = new TypeJudgement(getSupplierKey(), variablesTuple); - } -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java deleted file mode 100644 index 2c03a894..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java +++ /dev/null @@ -1,231 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.queries; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; -import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; -import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; -import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * Default implementation of PQuery. - * - * @author Bergmann Gabor - */ -public abstract class BasePQuery implements PQuery { - - protected PQueryStatus status = PQueryStatus.UNINITIALIZED; - /** - * @since 2.0 - */ - protected final PVisibility visibility; - protected List pProblems = new ArrayList(); - private List annotations = new ArrayList(); - private QueryEvaluationHint evaluationHints = new QueryEvaluationHint(null, (IQueryBackendFactory)null); - PDisjunction canonicalDisjunction; - private List parameterNames = null; // Lazy initialization - - /** For traceability only. */ - private List wrappingQuerySpecifications = new ArrayList(1); - - @Override - public Integer getPositionOfParameter(String parameterName) { - ensureInitialized(); - int index = getParameterNames().indexOf(parameterName); - return index != -1 ? index : null; - } - - protected void setStatus(PQueryStatus newStatus) { - this.status = newStatus; - } - - protected void addError(PProblem problem) { - status = PQueryStatus.ERROR; - pProblems.add(problem); - } - - @Override - public PQueryStatus getStatus() { - return status; - } - - @Override - public List getPProblems() { - return Collections.unmodifiableList(pProblems); - } - - @Override - public boolean isMutable() { - return status.equals(PQueryStatus.UNINITIALIZED) || status.equals(PQueryStatus.INITIALIZING); - } - - @Override - public void checkMutability() { - Preconditions.checkState(isMutable(), "Cannot edit query definition %s", getFullyQualifiedName()); - } - - /** - * @since 1.5 - */ - public void setEvaluationHints(QueryEvaluationHint hints) { - checkMutability(); - this.evaluationHints = hints; - } - - @Override - public QueryEvaluationHint getEvaluationHints() { - ensureInitialized(); - return evaluationHints; - // TODO instead of field, compute something from annotations? - } - - protected void addAnnotation(PAnnotation annotation) { - checkMutability(); - annotations.add(annotation); - } - - @Override - public List getAllAnnotations() { - ensureInitialized(); - return new ArrayList<>(annotations); - } - - private Stream getAnnotationStreamByName(final String name) { - ensureInitialized(); - return annotations.stream().filter(Objects::nonNull).filter(annotation -> Objects.equals(name, annotation.getName())); - } - - @Override - public List getAnnotationsByName(final String annotationName) { - return getAnnotationStreamByName(annotationName).collect(Collectors.toList()); - } - - @Override - public Optional getFirstAnnotationByName(String annotationName) { - return getAnnotationStreamByName(annotationName).findFirst(); - } - - @Override - public List getParameterNames() { - ensureInitialized(); - if (parameterNames == null) { - parameterNames = getParameters().stream().map(PParameter::getName).collect(Collectors.toList()); - } - return parameterNames; - } - - @Override - public Set getDirectReferredQueries() { - ensureInitialized(); - return canonicalDisjunction.getDirectReferredQueries(); - } - - @Override - public Set getAllReferredQueries() { - ensureInitialized(); - return canonicalDisjunction.getAllReferredQueries(); - } - - - @Override - public List publishedAs() { - return wrappingQuerySpecifications; - } - - @Override - public Set getTypeGuarantees() { - ensureInitialized(); - Set result = new HashSet(); - - List parameters = getParameters(); - for (int i=0; i bodies) { - canonicalDisjunction = new PDisjunction(this, bodies); - for (PBody body : canonicalDisjunction.getBodies()) { - body.setStatus(null); - } - setStatus(PQueryStatus.OK); - } - - /** - * Creates and returns the bodies of the query. If recalled again, a new instance is created. - * - * @return - * @throws ViatraQueryRuntimeException - */ - protected abstract Set doGetContainedBodies(); - - @Override - public String toString() { - return String.format("PQuery<%s>=%s", getFullyQualifiedName(), super.toString()); - } - - /** - * @since 2.0 - */ - @Override - public PVisibility getVisibility() { - return visibility; - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java deleted file mode 100644 index eae4eacf..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java +++ /dev/null @@ -1,104 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.queries; - -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; -import java.util.stream.Collectors; - -import tools.refinery.viatra.runtime.matchers.psystem.PBody; - -/** - * - * A disjunction is a set of bodies representing separate conditions. A {@link PQuery} has a single, canonical - * PDisjunction, that can be replaced using rewriter - * - * @author Zoltan Ujhelyi - * - */ -public class PDisjunction { - - private Set bodies; - private PQuery query; - - public PDisjunction(Set bodies) { - this(bodies.iterator().next().getPattern(), bodies); - } - - public PDisjunction(PQuery query, Set bodies) { - super(); - this.query = query; - this.bodies = Collections.unmodifiableSet(new LinkedHashSet<>(bodies)); - this.bodies.forEach(body -> body.setContainerDisjunction(this)); - } - - /** - * Returns an immutable set of bodies that consists of this disjunction - * - * @return the bodies - */ - public Set getBodies() { - return bodies; - } - - /** - * Returns the corresponding query specification. May be null if not set. - */ - public PQuery getQuery() { - return query; - } - - /** - * Returns all queries directly referred in the constraints. They are all required to evaluate this query - * - * @return a non-null, but possibly empty list of query definitions - */ - public Set getDirectReferredQueries() { - return this.getBodies().stream(). - flatMap(PQueries.directlyReferencedQueriesFunction()). // flatten stream of streams - collect(Collectors.toCollection(LinkedHashSet::new)); - } - - /** - * Returns all queries required to evaluate this query (transitively). - * - * @return a non-null, but possibly empty list of query definitions - */ - public Set getAllReferredQueries() { - Set processedQueries = new LinkedHashSet<>(); - processedQueries.add(this.getQuery()); - Set foundQueries = getDirectReferredQueries(); - Set newQueries = new LinkedHashSet<>(foundQueries); - - while(!processedQueries.containsAll(newQueries)) { - PQuery query = newQueries.iterator().next(); - processedQueries.add(query); - newQueries.remove(query); - Set referred = query.getDirectReferredQueries(); - referred.removeAll(processedQueries); - foundQueries.addAll(referred); - newQueries.addAll(referred); - } - return foundQueries; - } - - /** - * Decides whether a disjunction is mutable. A disjunction is mutable if all its contained bodies are mutable. - * - */ - public boolean isMutable() { - for (PBody body : bodies) { - if (!body.isMutable()) { - return false; - } - } - return true; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java deleted file mode 100644 index 07165aa2..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.queries; - -import java.util.Objects; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; - -/** - * A descriptor for declared PQuery parameters. A parameter has a name, a declared type and a direction constraint - * - * @author Zoltan Ujhelyi - * - */ -public class PParameter { - - private final String name; - private final String typeName; - private final IInputKey declaredUnaryType; - private final PParameterDirection direction; - - public PParameter(String name) { - this(name, (String) null); - } - - public PParameter(String name, String typeName) { - this(name, typeName, (IInputKey) null); - } - - public PParameter(String name, String typeName, IInputKey declaredUnaryType) { - this(name, typeName, declaredUnaryType, PParameterDirection.INOUT); - } - - /** - * @since 1.4 - */ - public PParameter(String name, String typeName, IInputKey declaredUnaryType, PParameterDirection direction) { - super(); - this.name = name; - this.typeName = typeName; - this.declaredUnaryType = declaredUnaryType; - this.direction = direction; - - if (declaredUnaryType != null && declaredUnaryType.getArity() != 1) { - throw new IllegalArgumentException( - "PParameter declared type must be unary instead of " + declaredUnaryType.getPrettyPrintableName()); - } - } - - /** - * @return the direction - * @since 1.4 - */ - public PParameterDirection getDirection() { - return direction; - } - - /** - * @return the name of the parameter - */ - public String getName() { - return name; - } - - /** - * Returns a textual representation of the declared type of the parameter - * - * @return the type description, or null if not available - */ - public String getTypeName() { - return typeName; - } - - /** - * Yield an {@link IInputKey} representation of the type declared for this parameter. - * - * @return the unary type that was declared on this parameter in the query header, or null if not available - */ - public IInputKey getDeclaredUnaryType() { - return declaredUnaryType; - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof PParameter) { - return Objects.equals(name, ((PParameter) obj).name) - && Objects.equals(typeName, ((PParameter) obj).typeName) - && Objects.equals(declaredUnaryType, ((PParameter) obj).declaredUnaryType) - && Objects.equals(direction, ((PParameter) obj).direction); - } - return false; - } - - @Override - public int hashCode() { - return Objects.hash(name, typeName, declaredUnaryType); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java deleted file mode 100644 index c94d4797..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.queries; - -/** - * Values of this enum describe a constraint to the calling of patterns regarding its parameters. - * - * @author Grill Balázs - * @since 1.4 - * - */ -public enum PParameterDirection { - - /** - * Default value, no additional constraint is applied - */ - INOUT, - - /** - * The parameters marked with this constraints shall be set to a value before calling the pattern - */ - IN, - - /** - * The parameters marked with this constraints shall not be set to a value before calling the pattern - */ - OUT - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java deleted file mode 100644 index 1fe4f541..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.queries; - -import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; - -/** - * Represents an error that was detected while the {@link PQuery} object was built from a source. - * @author Bergmann Gabor - * - */ -public class PProblem { - - private final String shortMessage; - private final String location; - private final Exception exception; - - public PProblem(String shortMessage) { - this(null, shortMessage, null, null); - } - /** - * @since 2.0 - */ - public PProblem(String shortMessage, Integer line, Integer column) { - this(null, shortMessage, line, column); - } - public PProblem(QueryProcessingException exception) { - this(exception, exception.getShortMessage(), null, null); - } - public PProblem(Exception exception, String shortMessage) { - this(exception, shortMessage, null, null); - } - - /** - * @since 2.0 - */ - public PProblem(Exception exception, String shortMessage, Integer line, Integer column) { - this.shortMessage = shortMessage; - this.exception = exception; - if (line == null) { - location = "Unspecified location"; - } else if (column == null) { - location = String.format("Line %d", line); - } else { - location = String.format("Line %d Column %d", line, column); - } - } - - public String getShortMessage() { - return shortMessage; - } - public Exception getException() { - return exception; - } - /** - * @since 2.0 - */ - public String getLocation() { - return location; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java deleted file mode 100644 index 56f8ca76..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.queries; - -import java.util.HashSet; -import java.util.Set; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference; -import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; - -/** - * Utility class for using PQueries in functional/streaming collection operations effectively - * - * @author Zoltan Ujhelyi - * - */ -public final class PQueries { - - /** - * Hidden constructor for utility class - */ - private PQueries() { - } - - /** - * Predicate checking for the status of selected queries - * - */ - public static Predicate queryStatusPredicate(final PQueryStatus status) { - return query -> query.getStatus().equals(status); - } - - /** - * Enumerates referred queries (without duplicates) for the given body - */ - public static Function> directlyReferencedQueriesFunction() { - return body -> (body.getConstraintsOfType(IMultiQueryReference.class).stream() - .flatMap(e -> e.getReferredQueries().stream()).distinct()); - } - - /** - * Enumerates directly referred extensional relations (without duplicates) in the canonical form of the given query - * - * @param enumerablesOnly - * only enumerable type constraints are considered - * @since 2.0 - */ - public static Stream directlyRequiredTypesOfQuery(PQuery query, boolean enumerablesOnly) { - return directlyRequiredTypesOfDisjunction(query.getDisjunctBodies(), enumerablesOnly); - } - - /** - * Enumerates directly referred extensional relations (without duplicates) for the given formulation of a query. - * - * @param enumerablesOnly - * only enumerable type constraints are considered - * @since 2.0 - */ - public static Stream directlyRequiredTypesOfDisjunction(PDisjunction disjunctBodies, - boolean enumerablesOnly) { - Class filterClass = enumerablesOnly ? TypeConstraint.class : ITypeConstraint.class; - return disjunctBodies.getBodies().stream().flatMap(body -> body.getConstraintsOfType(filterClass).stream()) - .map(constraint -> constraint.getEquivalentJudgement().getInputKey()).distinct(); - } - - /** - * @since 1.4 - */ - public static Predicate parameterDirectionPredicate(final PParameterDirection direction) { - return input -> input.getDirection() == direction; - } - - /** - * Returns all {@link PTraceable}s contained in the given {@link PQuery}: itself, its bodies and their constraints. - * - * @since 1.6 - */ - public static Set getTraceables(PQuery query) { - final Set traceables = new HashSet<>(); - traceables.add(query); - query.getDisjunctBodies().getBodies().forEach(body -> { - traceables.add(body); - body.getConstraints().forEach(traceables::add); - }); - return traceables; - } - - /** - * Calculates the simple name related from a given qualified name by finding the part after the last '.' character. - * - * @since 2.0 - */ - public static String calculateSimpleName(String qualifiedName) { - return qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java deleted file mode 100644 index a909c650..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java +++ /dev/null @@ -1,154 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.queries; - -import java.util.List; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; -import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; -import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; -import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; - -/** - * Internal representation of a query / graph pattern (using a constraint system formalism), - * to be interpreted by a query evaluator ({@link IQueryBackend}). - * End-users of VIATRA Query should access a query as an IQuerySpecification instead. - * - *

- * PQuerys are definitions of queries usable inside pattern descriptions. Such description always has (a non-null) name. The query - * itself is defined as a (non-empty) set of {@link PBody} instances, the result is the disjunction of the single - * {@link PBody} instances.

- *

- * A PQuery might be constructed from erroneous patterns or might be uninitialized - this is represented by its status. - * - * @author Zoltan Ujhelyi - * @since 0.8.0 - * @noimplement This interface is not intended to be implemented by clients. Use {@link BasePQuery} as a base class instead. - */ -public interface PQuery extends PQueryHeader, PTraceable { - - // TODO rewritten as / rewritten from traceability to PDisjunction? - - /** - * @author Zoltan Ujhelyi - * - */ - public enum PQueryStatus { - /** - * Marks that the query definition is not initialized - */ - UNINITIALIZED, - /** - * Marks that the query definition is being initialized - * @since 1.4 - */ - INITIALIZING, - /** - * The query definition was successfully initialized - */ - OK, - /** - * The query definition was initialized, but some issues were present - */ - WARNING, - /** - * The query definition was not successfully initialized because of an error - */ - ERROR - } - - /** - * Returns all bodies associated with the query in their canonical form. If called multiple times, the same set with - * the same contents will be returned. - * - */ - PDisjunction getDisjunctBodies(); - - /** - * Returns all queries directly referred in the constraints. They are all required to evaluate this query - * - * @return a non-null, but possibly empty list of query definitions - */ - Set getDirectReferredQueries(); - - /** - * Returns all queries required to evaluate this query (transitively). - * - * @return a non-null, but possibly empty list of query definitions - */ - Set getAllReferredQueries(); - - /** - * Returns the initialization status of the definition - * - */ - PQueryStatus getStatus(); - - /** - * Returns a list describing the problems that were found in this query. - * - *

TODO: formulate invariant connecting {@link #getPProblems()} and {@link #getStatus()}. - * - * @return a non-null, but possibly empty list of problems - */ - List getPProblems(); - - /** - * Before a modification operation is executed, a mutability check is performed (via the {@link #getStatus()} - * implementation, and in case of problems an {@link IllegalStateException} is thrown. - */ - void checkMutability(); - - /** - * An option to check mutability of the query. It can be used to avoid getting an {@link IllegalStateException} by - * the execution of {@link #checkMutability()}. - * - * @return true if the query specification is still editable - */ - boolean isMutable(); - - /** - * Optional hints regarding the query evaluation strategy, to be interpreted by the query engine. - *

To ensure the possibility of external overrides, - * the evaluation engine should not directly consult this field, - * but use an {@link IQueryBackendHintProvider} instead. - */ - public QueryEvaluationHint getEvaluationHints(); - - - /** - * Type information, expressed on query parameters, that all matches of the query are guaranteed to respect. - *

At the very minimum, this should include the declared types of the parameters. - *

The type judgement tuples shall contain the parameter index, NOT the {@link PParameter} object. - * - * @return a non-null set of type judgements that the query guarantees for its matches - */ - public Set getTypeGuarantees(); - - /** - * If the query definition is uninitialized, initializes it. - * @throws ViatraQueryRuntimeException if initialization of query specification fails - */ - public abstract void ensureInitialized(); - - /** - * Returns the end-user query specification API objects that wrap this query. - * - *

Intended for traceability and debug purposes, not part of normal operation. - * Returned list is intended to be appended during query specification construction time. - * - * @return a non-null, but possibly empty list of query specification objects; - */ - List publishedAs(); - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java deleted file mode 100644 index f3671934..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java +++ /dev/null @@ -1,101 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.queries; - -import java.util.List; -import java.util.Optional; - -import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; - -/** - * Represents header information (metainfo) about a query. - *

To be implemented both by IQuerySpecifications intended for end users, - * and the internal query representation {@link PQuery}. - * - * - * @author Bergmann Gabor - * @since 0.9 - */ -public interface PQueryHeader { - - /** - * Identifies the pattern for which matchers can be instantiated. - */ - public String getFullyQualifiedName(); - - /** - * Return the list of parameter names - * - * @return a non-null, but possibly empty list of parameter names - */ - public List getParameterNames(); - - /** - * Returns a list of parameter descriptions - * - * @return a non-null, but possibly empty list of parameter descriptions - */ - public List getParameters(); - - /** - * Returns the index of a named parameter - * - * @param parameterName - * @return the index, or null of no such parameter is available - */ - public Integer getPositionOfParameter(String parameterName); - - /** - * Returns a parameter by name if exists - * @since 2.1 - */ - default Optional getParameter(String parameterName) { - return Optional.ofNullable(getPositionOfParameter(parameterName)) - .map(getParameters()::get); - } - - /** - * Returns the list of annotations specified for this query - * - * @return a non-null, but possibly empty list of annotations - */ - public List getAllAnnotations(); - - /** - * Returns the list of annotations with a specified name - * - * @param annotationName - * @return a non-null, but possibly empty list of annotations - */ - public List getAnnotationsByName(String annotationName); - - /** - * Returns the first annotation with a specified name - * - * @since 2.0 - */ - public Optional getFirstAnnotationByName(String annotationName); - - /** - * Returns the visibility information about the query. - * @since 2.0 - */ - public PVisibility getVisibility(); - - /** - * Returns the non-qualified name of the query. By default this means returning the qualified name after the last - * '.' character. - * - * @since 2.0 - */ - public default String getSimpleName() { - return PQueries.calculateSimpleName(getFullyQualifiedName()); - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java deleted file mode 100644 index 7cb312bd..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.queries; - -/** - * @author Zoltan Ujhelyi - * @since 2.0 - * - */ -public enum PVisibility { - - /** - * A public (default) visibility means a pattern can be called at any time. - */ - PUBLIC, - /** - * A private query is not expected to be called directly, only by a different query matcher. - */ - PRIVATE, - /** - * A query that is only used inside a single caller query and is not visible outside its container query. Such - * patterns must also fulfill the following additional constraints: - * - *

    - *
  • An embedded query must have only a single body.
  • - *
  • An embedded query must not be recursice.
  • - *
- */ - EMBEDDED - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java deleted file mode 100644 index 470d7287..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java +++ /dev/null @@ -1,35 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.queries; - -import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; - -/** - * Represent an exception that occurred while initializing the specification of a query. - * @author Bergmann Gabor - * @since 0.9 - * - */ -public class QueryInitializationException extends QueryProcessingException { - - public QueryInitializationException(String message, String[] context, String shortMessage, Object patternDescription, - Throwable cause) { - super(message, context, shortMessage, patternDescription, cause); - } - - public QueryInitializationException(String message, String[] context, String shortMessage, Object patternDescription) { - super(message, context, shortMessage, patternDescription); - } - - private static final long serialVersionUID = 9106033062252951489L; - - - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java deleted file mode 100644 index 276b2b42..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java +++ /dev/null @@ -1,53 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import java.util.Objects; - -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; - -/** - * @since 1.6 - * - */ -public class AbstractRewriterTraceSource { - - private IRewriterTraceCollector traceCollector = NopTraceCollector.INSTANCE; - - public void setTraceCollector(IRewriterTraceCollector traceCollector) { - this.traceCollector = Objects.requireNonNull(traceCollector); - } - - public IPTraceableTraceProvider getTraces() { - return traceCollector; - } - - protected IRewriterTraceCollector getTraceCollector() { - return traceCollector; - } - - /** - * Mark the given derivative to be originated from the given original constraint. - * @since 1.6 - */ - protected void addTrace(PTraceable original, PTraceable derivative){ - traceCollector.addTrace(original, derivative); - } - - /** - * Indicate that the given derivative is removed from the resulting query, thus its trace - * information should be removed also. - * @since 1.6 - */ - protected void derivativeRemoved(PConstraint derivative, IDerivativeModificationReason reason){ - traceCollector.derivativeRemoved(derivative, reason); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java deleted file mode 100644 index 237a762d..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -/** - * Common reasons for removing constraint through rewriters - * - * @noreference This enum is not intended to be referenced by clients. - */ -public enum ConstraintRemovalReason implements IDerivativeModificationReason { - - MOOT_EQUALITY, - WEAK_INEQUALITY_SELF_LOOP, - TYPE_SUBSUMED, - DUPLICATE - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java deleted file mode 100644 index 3b5d7390..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; - -/** - * @author Marton Bur - * - */ -public class DefaultFlattenCallPredicate implements IFlattenCallPredicate { - - @Override - public boolean shouldFlatten(PositivePatternCall positivePatternCall) { - return true; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java deleted file mode 100644 index 06b8d372..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java +++ /dev/null @@ -1,129 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Collectors; - -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * This rewriter class can add new equality constraints to the copied body - * - * @author Marton Bur - * - */ -class FlattenerCopier extends PBodyCopier { - - private final Map calls; - - private static class CallInformation { - final PBody body; - final Map variableMapping; - - private CallInformation(PBody body) { - this.body = body; - this.variableMapping = new HashMap<>(); - } - } - - public FlattenerCopier(PQuery query, Map callsToFlatten) { - super(query); - this.calls = callsToFlatten.entrySet().stream().collect(Collectors.toMap(Entry::getKey, entry -> new CallInformation(entry.getValue()))); - } - - protected void copyVariable(PositivePatternCall contextPatternCall, PVariable variable, String newName) { - PVariable newPVariable = body.getOrCreateVariableByName(newName); - calls.get(contextPatternCall).variableMapping.put(variable, newPVariable); - variableMapping.put(variable, newPVariable); - } - - /** - * Merge all variables and constraints from the body called through the given pattern call to a target body. If - * multiple bodies are merged into a single one, use the renamer and filter options to avoid collisions. - * - * @param sourceBody - * @param namingTool - * @param filter - */ - public void mergeBody(PositivePatternCall contextPatternCall, IVariableRenamer namingTool, - IConstraintFilter filter) { - - PBody sourceBody = calls.get(contextPatternCall).body; - - // Copy variables - Set allVariables = sourceBody.getAllVariables(); - for (PVariable pVariable : allVariables) { - if (pVariable.isUnique()) { - copyVariable(contextPatternCall, pVariable, - namingTool.createVariableName(pVariable, sourceBody.getPattern())); - } - } - - // Copy constraints which are not filtered - Set constraints = sourceBody.getConstraints(); - for (PConstraint pConstraint : constraints) { - if (!(pConstraint instanceof ExportedParameter) && !filter.filter(pConstraint)) { - copyConstraint(pConstraint); - } - } - } - - @Override - protected void copyPositivePatternCallConstraint(PositivePatternCall positivePatternCall) { - - if (!calls.containsKey(positivePatternCall)) { - // If the call was not flattened, copy the constraint - super.copyPositivePatternCallConstraint(positivePatternCall); - } else { - PBody calledBody = Objects.requireNonNull(calls.get(positivePatternCall).body); - Preconditions.checkArgument(positivePatternCall.getReferredQuery().equals(calledBody.getPattern())); - - List symbolicParameters = calledBody.getSymbolicParameterVariables(); - Object[] elements = positivePatternCall.getVariablesTuple().getElements(); - for (int i = 0; i < elements.length; i++) { - // Create equality constraints between the caller PositivePatternCall and the corresponding body - // parameter variables - createEqualityConstraint((PVariable) elements[i], symbolicParameters.get(i), positivePatternCall); - } - - } - } - - private void createEqualityConstraint(PVariable pVariable1, PVariable pVariable2, - PositivePatternCall contextPatternCall) { - PVariable who = variableMapping.get(pVariable1); - PVariable withWhom = calls.get(contextPatternCall).variableMapping.get(pVariable2); - addTrace(contextPatternCall, new Equality(body, who, withWhom)); - } - - @Override - protected void copyExpressionEvaluationConstraint(final ExpressionEvaluation expressionEvaluation) { - Map variableMapping = this.variableMapping.entrySet().stream() - .filter(input -> expressionEvaluation.getPSystem().getAllVariables().contains(input.getKey())) - .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); - - PVariable mappedOutputVariable = variableMapping.get(expressionEvaluation.getOutputVariable()); - addTrace(expressionEvaluation, new ExpressionEvaluation(body, new VariableMappingExpressionEvaluatorWrapper(expressionEvaluation.getEvaluator(), variableMapping), mappedOutputVariable, expressionEvaluation.isUnwinding())); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java deleted file mode 100644 index 518b9c64..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; - -/** - * Helper interface to exclude constraints from PBody copy processes - * - * @author Marton Bur - * - */ -public interface IConstraintFilter { - /** - * Returns true, if the given constraint should be filtered (thus should not be copied) - * - * @param constraint - * to check - * @return true, if the constraint should be filtered - */ - boolean filter(PConstraint constraint); - - public static class ExportedParameterFilter implements IConstraintFilter { - - @Override - public boolean filter(PConstraint constraint) { - return constraint instanceof ExportedParameter; - } - - } - - public static class AllowAllFilter implements IConstraintFilter { - - @Override - public boolean filter(PConstraint constraint) { - // Nothing is filtered - return false; - } - - } -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java deleted file mode 100644 index dbd6a78d..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java +++ /dev/null @@ -1,19 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -/** - * This is a role indication interface, implementations may provide a reason about - * why a modification is made during PQuery normalization. - * @since 1.6 - * - */ -public interface IDerivativeModificationReason { - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java deleted file mode 100644 index 7e224e98..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; - - -/** - * Interface used by the PQueryFlattener to decide which positive pattern calls to flatten - * - * @author Marton Bur - * - */ -public interface IFlattenCallPredicate { - - /** - * Decides whether the called query by the pattern call should be flattened into the caller or not. - * - * @param positivePatternCall - * the pattern call - * @return true if the call should be flattened - */ - boolean shouldFlatten(PositivePatternCall positivePatternCall); - - /** - * Flattens only if all operand predicates vote for flattening. - * @author Gabor Bergmann - * @since 2.1 - */ - public static class And implements IFlattenCallPredicate { - private IFlattenCallPredicate[] operands; - public And(IFlattenCallPredicate... operands) { - this.operands = operands; - } - - @Override - public boolean shouldFlatten(PositivePatternCall positivePatternCall) { - for (IFlattenCallPredicate operand : operands) { - if (!operand.shouldFlatten(positivePatternCall)) return false; - } - return true; - } - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java deleted file mode 100644 index 84da4d1b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * This interface provides methods to trace the {@link PTraceable}s of a transformed {@link PQuery} produced by - * a {@link PDisjunctionRewriter}. In case the associated rewriter is a composite (a.k.a. {@link PDisjunctionRewriterCacher}), - * this trace provider handles traces end-to-end, hiding all the intermediate transformation steps. - * - * @since 1.6 - * @noimplement This interface is not intended to be implemented by clients. - */ -public interface IPTraceableTraceProvider { - - /** - * Find and return the canonical {@link PTraceable}s in the original query which are the sources of the given derivative - * {@link PTraceable} according to the transformation. - * - * @param derivative a {@link PTraceable} which is contained by the {@link PQuery} produced by the associated rewriter - * @since 2.0 - */ - public Stream getCanonicalTraceables(PTraceable derivative); - - /** - * Find and return the {@link PTraceable}s in the rewritten query which are the destinations of the given source - * {@link PTraceable} according to the transformation. - * - * @param source a {@link PTraceable} which is contained by a {@link PQuery} before rewriting - * @since 2.0 - */ - public Stream getRewrittenTraceables(PTraceable source); - - /** - * Returns whether the given traceable element has been removed by every rewriter for a reason. - */ - public boolean isRemoved(PTraceable traceable); - - /** - * Returns the reasons for which the traceable element has been removed by the rewriters. - * @return the reasons of removal during rewriting - * @since 2.0 - */ - public Stream getRemovalReasons(PTraceable traceable); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java deleted file mode 100644 index 70771ea7..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; - -/** - * This is the internal API of {@link IPTraceableTraceProvider} expected to be used by - * copier and rewriter implementations. - * - * @since 1.6 - * @noreference This interface is not intended to be referenced by clients. - */ -public interface IRewriterTraceCollector extends IPTraceableTraceProvider { - - /** - * Mark the given derivative to be originated from the given original constraint. - */ - public void addTrace(PTraceable origin, PTraceable derivative); - - /** - * Indicate that the given derivative is removed from the resulting query, thus its trace - * information should be removed also. - */ - public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java deleted file mode 100644 index ce446e0d..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * Helper interface to ease the naming of the new variables during flattening - * - * @author Marton Bur - * - */ -public interface IVariableRenamer { - /** - * Creates a variable name based on a given variable and a given query. It only creates a String, doesn't set - * anything. - * - * @param pVariable - * @param query - * @return the new variable name as a String - */ - String createVariableName(PVariable pVariable, PQuery query); - - public class SameName implements IVariableRenamer { - @Override - public String createVariableName(PVariable pVariable, PQuery query) { - return pVariable.getName(); - } - } - - public class HierarchicalName implements IVariableRenamer { - - private int callCount; - - public void setCallCount(int callCount) { - this.callCount = callCount; - } - - @Override - public String createVariableName(PVariable pVariable, PQuery query) { - // make sure to keep the "_" prefix before anonymous variables - String newVarName = getShortName(query) + "<" + callCount + ">" + "_" + pVariable.getName(); - return pVariable.getName().startsWith("_") ? "_" + newVarName : newVarName ; - } - - private String getShortName(PQuery query) { - String fullyQualifiedName = query.getFullyQualifiedName(); - int beginIndex = fullyQualifiedName.lastIndexOf('.') + 1; - return fullyQualifiedName.substring(beginIndex); - } - } -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java deleted file mode 100644 index 7429fc60..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java +++ /dev/null @@ -1,135 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Queue; -import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * Multimap-based implementation to contain and query traces - * - * @since 1.6 - * - */ -public class MappingTraceCollector implements IRewriterTraceCollector { - - /** - * Traces from derivative to original - */ - private final IMultiLookup traces = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); - - /** - * Traces from original to derivative - */ - private final IMultiLookup inverseTraces = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); - - /** - * Reasons for removing {@link PTraceable}s - */ - private final Map removals = new HashMap<>(); - - /** - * Decides whether {@link PTraceable} is removed - */ - private final Predicate removed = removals::containsKey; - - /** - * @since 2.0 - */ - @Override - public Stream getCanonicalTraceables(PTraceable derivative) { - return findTraceEnds(derivative, traces).stream(); - } - - /** - * @since 2.0 - */ - @Override - public Stream getRewrittenTraceables(PTraceable source) { - return findTraceEnds(source, inverseTraces).stream(); - } - - /** - * Returns the end of trace chains starting from the given {@link PTraceable} along the given trace edges. - */ - private Set findTraceEnds(PTraceable traceable, IMultiLookup traceRecords) { - if (traceable instanceof PQuery) { // PQueries are preserved - return Collections.singleton(traceable); - } - Set visited = new HashSet<>(); - Set result = new HashSet<>(); - Queue queue = new LinkedList<>(); - queue.add(traceable); - while(!queue.isEmpty()){ - PTraceable aDerivative = queue.poll(); - // Track visited elements to avoid infinite loop via directed cycles in traces - visited.add(aDerivative); - IMemoryView nextOrigins = traceRecords.lookup(aDerivative); - if (nextOrigins == null){ - // End of trace chain - result.add(aDerivative); - } else { - // Follow traces - for(PTraceable nextOrigin : nextOrigins){ - if (!visited.contains(nextOrigin)){ - queue.add(nextOrigin); - } - } - } - } - return result; - } - - @Override - public void addTrace(PTraceable original, PTraceable derivative){ - traces.addPairOrNop(derivative, original); - inverseTraces.addPairOrNop(original, derivative); - // Even if this element was marked as removed earlier, now we replace it with another constraint! - removals.remove(original); - } - - @Override - public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason){ - Preconditions.checkState(!removals.containsKey(derivative), "Traceable %s removed multiple times", derivative); - // XXX the derivative must not be removed from the trace chain, as some rewriters, e.g. the normalizer keeps trace links to deleted elements - if (!inverseTraces.lookupExists(derivative)) { - // If there already exists a trace link, this removal means an update - removals.put(derivative, reason); - } - } - - @Override - public boolean isRemoved(PTraceable traceable) { - return getRewrittenTraceables(traceable).allMatch(removed); - } - - /** - * @since 2.0 - */ - @Override - public Stream getRemovalReasons(PTraceable traceable) { - return getRewrittenTraceables(traceable).filter(removed).map(removals::get); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java deleted file mode 100644 index 96c0b205..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; - -/** - * @author Grill Balázs - * @since 1.4 - * - */ -public class NeverFlattenCallPredicate implements IFlattenCallPredicate { - - - @Override - public boolean shouldFlatten(PositivePatternCall positivePatternCall) { - return false; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java deleted file mode 100644 index 15cf577e..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java +++ /dev/null @@ -1,68 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; - -/** - * This implementation does not store any traces and scales to NOP for every traceability feature. - * @since 1.6 - * - */ -public class NopTraceCollector implements IRewriterTraceCollector { - - public static final IRewriterTraceCollector INSTANCE = new NopTraceCollector(); - - private NopTraceCollector() { - // Private constructor to force using the common instance - } - - /** - * @since 2.0 - */ - @Override - public Stream getCanonicalTraceables(PTraceable derivative) { - return Stream.empty(); - } - - /** - * @since 2.0 - */ - @Override - public Stream getRewrittenTraceables(PTraceable source) { - return Stream.empty(); - } - - - @Override - public void addTrace(PTraceable origin, PTraceable derivative) { - // ignored - } - - @Override - public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason) { - // ignored - } - - @Override - public boolean isRemoved(PTraceable traceable) { - return false; - } - - /** - * @since 2.0 - */ - @Override - public Stream getRemovalReasons(PTraceable traceable) { - return Stream.empty(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java deleted file mode 100644 index e66c4eea..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java +++ /dev/null @@ -1,306 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; -import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter; -import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; - -import java.util.*; -import java.util.stream.Collectors; - -/** - * This class can create a new PBody for a PQuery. The result body contains a copy of given variables and constraints. - * - * @author Marton Bur - * - */ -public class PBodyCopier extends AbstractRewriterTraceSource { - - /** - * The created body - */ - protected PBody body; - /** - * Mapping between the original and the copied variables - */ - protected Map variableMapping = new HashMap<>(); - - public Map getVariableMapping() { - return variableMapping; - } - - /** - * @since 1.6 - */ - public PBodyCopier(PBody body, IRewriterTraceCollector traceCollector) { - this.body = new PBody(body.getPattern()); - setTraceCollector(traceCollector); - - // do the actual copying - mergeBody(body); - } - - /** - * @since 1.6 - */ - public PBodyCopier(PQuery query) { - this.body = new PBody(query); - } - - public void mergeBody(PBody sourceBody) { - mergeBody(sourceBody, new SameName(), new AllowAllFilter()); - } - - /** - * Merge all variables and constraints from a source body to a target body. If multiple bodies are merged into a - * single one, use the renamer and filter options to avoid collisions. - */ - public void mergeBody(PBody sourceBody, IVariableRenamer namingTool, IConstraintFilter filter) { - - // Copy variables - Set allVariables = sourceBody.getAllVariables(); - for (PVariable pVariable : allVariables) { - if (pVariable.isUnique()) { - copyVariable(pVariable, namingTool.createVariableName(pVariable, sourceBody.getPattern())); - } - } - - // Copy exported parameters - this.body.setSymbolicParameters(sourceBody.getSymbolicParameters().stream() - .map(this::copyExportedParameterConstraint).collect(Collectors.toList())); - - // Copy constraints which are not filtered - Set constraints = sourceBody.getConstraints(); - for (PConstraint pConstraint : constraints) { - if (!(pConstraint instanceof ExportedParameter) && !filter.filter(pConstraint)) { - copyConstraint(pConstraint); - } - } - - // Add trace between original and copied body - addTrace(sourceBody, body); - } - - protected void copyVariable(PVariable variable, String newName) { - PVariable newPVariable = body.getOrCreateVariableByName(newName); - variableMapping.put(variable, newPVariable); - } - - /** - * Returns the body with the copied variables and constraints. The returned body is still uninitialized. - */ - public PBody getCopiedBody() { - return body; - } - - protected void copyConstraint(PConstraint constraint) { - if (constraint instanceof ExportedParameter) { - copyExportedParameterConstraint((ExportedParameter) constraint); - } else if (constraint instanceof Equality) { - copyEqualityConstraint((Equality) constraint); - } else if (constraint instanceof Inequality) { - copyInequalityConstraint((Inequality) constraint); - } else if (constraint instanceof TypeConstraint) { - copyTypeConstraint((TypeConstraint) constraint); - } else if (constraint instanceof TypeFilterConstraint) { - copyTypeFilterConstraint((TypeFilterConstraint) constraint); - } else if (constraint instanceof ConstantValue) { - copyConstantValueConstraint((ConstantValue) constraint); - } else if (constraint instanceof PositivePatternCall) { - copyPositivePatternCallConstraint((PositivePatternCall) constraint); - } else if (constraint instanceof NegativePatternCall) { - copyNegativePatternCallConstraint((NegativePatternCall) constraint); - } else if (constraint instanceof BinaryTransitiveClosure) { - copyBinaryTransitiveClosureConstraint((BinaryTransitiveClosure) constraint); - } else if (constraint instanceof RepresentativeElectionConstraint) { - copyRepresentativeElectionConstraint((RepresentativeElectionConstraint) constraint); - } else if (constraint instanceof RelationEvaluation) { - copyRelationEvaluationConstraint((RelationEvaluation) constraint); - } else if (constraint instanceof BinaryReflexiveTransitiveClosure) { - copyBinaryReflexiveTransitiveClosureConstraint((BinaryReflexiveTransitiveClosure) constraint); - } else if (constraint instanceof PatternMatchCounter) { - copyPatternMatchCounterConstraint((PatternMatchCounter) constraint); - } else if (constraint instanceof AggregatorConstraint) { - copyAggregatorConstraint((AggregatorConstraint) constraint); - } else if (constraint instanceof ExpressionEvaluation) { - copyExpressionEvaluationConstraint((ExpressionEvaluation) constraint); - } else { - throw new QueryProcessingException("Unknown PConstraint {0} encountered while copying PBody", - new String[] { constraint.getClass().getName() }, "Unknown PConstraint", body.getPattern()); - } - } - - protected ExportedParameter copyExportedParameterConstraint(ExportedParameter exportedParameter) { - PVariable mappedPVariable = variableMapping.get(exportedParameter.getParameterVariable()); - PParameter parameter = exportedParameter.getPatternParameter(); - ExportedParameter newExportedParameter; - newExportedParameter = new ExportedParameter(body, mappedPVariable, parameter); - body.getSymbolicParameters().add(newExportedParameter); - addTrace(exportedParameter, newExportedParameter); - return newExportedParameter; - } - - protected void copyEqualityConstraint(Equality equality) { - PVariable who = equality.getWho(); - PVariable withWhom = equality.getWithWhom(); - addTrace(equality, new Equality(body, variableMapping.get(who), variableMapping.get(withWhom))); - } - - protected void copyInequalityConstraint(Inequality inequality) { - PVariable who = inequality.getWho(); - PVariable withWhom = inequality.getWithWhom(); - addTrace(inequality, new Inequality(body, variableMapping.get(who), variableMapping.get(withWhom))); - } - - protected void copyTypeConstraint(TypeConstraint typeConstraint) { - PVariable[] mappedVariables = extractMappedVariables(typeConstraint); - Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); - addTrace(typeConstraint, new TypeConstraint(body, variablesTuple, typeConstraint.getSupplierKey())); - } - - protected void copyTypeFilterConstraint(TypeFilterConstraint typeConstraint) { - PVariable[] mappedVariables = extractMappedVariables(typeConstraint); - Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); - addTrace(typeConstraint, new TypeFilterConstraint(body, variablesTuple, typeConstraint.getInputKey())); - } - - protected void copyConstantValueConstraint(ConstantValue constantValue) { - PVariable pVariable = (PVariable) constantValue.getVariablesTuple().getElements()[0]; - addTrace(constantValue, - new ConstantValue(body, variableMapping.get(pVariable), constantValue.getSupplierKey())); - } - - protected void copyPositivePatternCallConstraint(PositivePatternCall positivePatternCall) { - PVariable[] mappedVariables = extractMappedVariables(positivePatternCall); - Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); - addTrace(positivePatternCall, - new PositivePatternCall(body, variablesTuple, positivePatternCall.getReferredQuery())); - } - - protected void copyNegativePatternCallConstraint(NegativePatternCall negativePatternCall) { - PVariable[] mappedVariables = extractMappedVariables(negativePatternCall); - Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); - addTrace(negativePatternCall, - new NegativePatternCall(body, variablesTuple, negativePatternCall.getReferredQuery())); - } - - protected void copyBinaryTransitiveClosureConstraint(BinaryTransitiveClosure binaryTransitiveClosure) { - PVariable[] mappedVariables = extractMappedVariables(binaryTransitiveClosure); - Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); - addTrace(binaryTransitiveClosure, - new BinaryTransitiveClosure(body, variablesTuple, binaryTransitiveClosure.getReferredQuery())); - } - - protected void copyRepresentativeElectionConstraint(RepresentativeElectionConstraint constraint) { - var mappedVariables = extractMappedVariables(constraint); - var variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); - addTrace(constraint, new RepresentativeElectionConstraint(body, variablesTuple, constraint.getReferredQuery(), - constraint.getConnectivity())); - } - - /** - * @since 2.8 - */ - protected void copyRelationEvaluationConstraint(RelationEvaluation relationEvaluation) { - PVariable[] mappedVariables = extractMappedVariables(relationEvaluation); - Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); - addTrace(relationEvaluation, new RelationEvaluation(body, variablesTuple, relationEvaluation.getReferredQueries(), - relationEvaluation.getEvaluator())); - } - - /** - * @since 2.0 - */ - protected void copyBinaryReflexiveTransitiveClosureConstraint( - BinaryReflexiveTransitiveClosure binaryReflexiveTransitiveClosure) { - PVariable[] mappedVariables = extractMappedVariables(binaryReflexiveTransitiveClosure); - Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); - addTrace(binaryReflexiveTransitiveClosure, - new BinaryReflexiveTransitiveClosure(body, variablesTuple, - binaryReflexiveTransitiveClosure.getReferredQuery(), - binaryReflexiveTransitiveClosure.getUniverseType())); - } - - protected void copyPatternMatchCounterConstraint(PatternMatchCounter patternMatchCounter) { - PVariable[] mappedVariables = extractMappedVariables(patternMatchCounter); - PVariable mappedResultVariable = variableMapping.get(patternMatchCounter.getResultVariable()); - Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); - addTrace(patternMatchCounter, new PatternMatchCounter(body, variablesTuple, - patternMatchCounter.getReferredQuery(), mappedResultVariable)); - } - - /** - * @since 1.4 - */ - protected void copyAggregatorConstraint(AggregatorConstraint constraint) { - PVariable[] mappedVariables = extractMappedVariables(constraint); - PVariable mappedResultVariable = variableMapping.get(constraint.getResultVariable()); - Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); - addTrace(constraint, new AggregatorConstraint(constraint.getAggregator(), body, variablesTuple, - constraint.getReferredQuery(), mappedResultVariable, constraint.getAggregatedColumn())); - } - - protected void copyExpressionEvaluationConstraint(ExpressionEvaluation expressionEvaluation) { - PVariable mappedOutputVariable = variableMapping.get(expressionEvaluation.getOutputVariable()); - addTrace(expressionEvaluation, new ExpressionEvaluation(body, - new VariableMappingExpressionEvaluatorWrapper(expressionEvaluation.getEvaluator(), variableMapping), - mappedOutputVariable, expressionEvaluation.isUnwinding())); - } - - /** - * For positive pattern calls - * - * @param positivePatternCall - * @return the mapped variables to the pattern's parameters - */ - protected PVariable[] extractMappedVariables(EnumerablePConstraint enumerablePConstraint) { - Object[] pVariables = enumerablePConstraint.getVariablesTuple().getElements(); - return mapVariableList(pVariables); - } - - /** - * For negative and count pattern calls. - * - * @param patternMatchCounter - * @return the mapped variables to the pattern's parameters - */ - private PVariable[] extractMappedVariables(PatternCallBasedDeferred patternCallBasedDeferred) { - Object[] pVariables = patternCallBasedDeferred.getActualParametersTuple().getElements(); - return mapVariableList(pVariables); - } - - /** - * For type filters. - */ - private PVariable[] extractMappedVariables(TypeFilterConstraint typeFilterConstraint) { - Object[] pVariables = typeFilterConstraint.getVariablesTuple().getElements(); - return mapVariableList(pVariables); - } - - private PVariable[] mapVariableList(Object[] pVariables) { - List list = new ArrayList(); - for (int i = 0; i < pVariables.length; i++) { - PVariable mappedVariable = variableMapping.get(pVariables[i]); - list.add(mappedVariable); - } - return list.toArray(new PVariable[0]); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java deleted file mode 100644 index 90943129..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java +++ /dev/null @@ -1,310 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Queue; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; -import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; -import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; -import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; -import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; - -/** - * A disjunction rewriter for creating a normalized form of specification, unifying variables and running basic sanity - * checks. This rewriter does not copy but modifies directly the original specification, requiring a mutable - * disjunction. - * - * @author Gabor Bergmann - * - */ -public class PBodyNormalizer extends PDisjunctionRewriter { - - private IQueryMetaContext context; - - public PBodyNormalizer(IQueryMetaContext context) { - this.context = context; - } - - /** - * Returns whether unary constraint elimination is enabled. This behavior can be customized by creating a subclass - * with a custom implementation. - * - * @since 1.6 - */ - protected boolean shouldCalculateImpliedTypes(PQuery query) { - return true; - } - - /** - * Returns whether 'weakened alternative' suggestions of the context shall be expanded as additional PConstraints. - * This behavior can be customized by creating a subclass - * with a custom implementation. - * - * @since 1.6 - */ - protected boolean shouldExpandWeakenedAlternatives(PQuery query) { - return false; - } - - @Override - public PDisjunction rewrite(PDisjunction disjunction) { - Set normalizedBodies = new LinkedHashSet<>(); - for (PBody body : disjunction.getBodies()) { - PBodyCopier copier = new PBodyCopier(body, getTraceCollector()); - PBody modifiedBody = copier.getCopiedBody(); - normalizeBody(modifiedBody); - normalizedBodies.add(modifiedBody); - modifiedBody.setStatus(PQueryStatus.OK); - } - return new PDisjunction(normalizedBodies); - } - - public void setContext(IQueryMetaContext context) { - this.context = context; - } - - /** - * Provides a normalized version of the pattern body. May return a different version than the original version if - * needed. - * - * @param body - */ - public PBody normalizeBody(PBody body) { - try { - return normalizeBodyInternal(body); - } catch (QueryProcessingException e) { - throw new RewriterException("Error during rewriting: {1}", new String[] { e.getMessage() }, - e.getShortMessage(), body.getPattern(), e); - } - } - - PBody normalizeBodyInternal(PBody body) { - // UNIFICATION AND WEAK INEQUALITY ELIMINATION - unifyVariablesAlongEqualities(body); - eliminateWeakInequalities(body); - removeMootEqualities(body); - - // ADDING WEAKENED ALTERNATIVES - if (shouldExpandWeakenedAlternatives(body.getPattern())) { - expandWeakenedAlternativeConstraints(body); - } - - // CONSTRAINT ELIMINATION WITH TYPE INFERENCE - if (shouldCalculateImpliedTypes(body.getPattern())) { - eliminateInferrableTypes(body, context); - } else { - // ELIMINATE DUPLICATE TYPE CONSTRAINTS - eliminateDuplicateTypeConstraints(body); - } - - - // PREVENTIVE CHECKS - checkSanity(body); - return body; - } - - private void removeMootEqualities(PBody body) { - Set equals = body.getConstraintsOfType(Equality.class); - for (Equality equality : equals) { - if (equality.isMoot()) { - equality.delete(); - derivativeRemoved(equality, ConstraintRemovalReason.MOOT_EQUALITY); - } - } - } - - /** - * Unifies allVariables along equalities so that they can be handled as one. - * - * @param body - */ - void unifyVariablesAlongEqualities(PBody body) { - Set equals = body.getConstraintsOfType(Equality.class); - for (Equality equality : equals) { - if (!equality.isMoot()) { - equality.getWho().unifyInto(equality.getWithWhom()); - } - } - } - - /** - * Eliminates weak inequalities if they are not substantiated. - * - * @param body - */ - void eliminateWeakInequalities(PBody body) { - for (Inequality inequality : body.getConstraintsOfType(Inequality.class)){ - if (inequality.isEliminable()){ - inequality.eliminateWeak(); - derivativeRemoved(inequality, ConstraintRemovalReason.WEAK_INEQUALITY_SELF_LOOP); - } - } - } - - /** - * Eliminates all type constraints that are inferrable from other constraints. - */ - void eliminateInferrableTypes(final PBody body, IQueryMetaContext context) { - Set subsumedByRetainedConstraints = new HashSet(); - LinkedList allTypeConstraints = new LinkedList(); - for (PConstraint pConstraint : body.getConstraints()) { - if (pConstraint instanceof ITypeConstraint) { - allTypeConstraints.add((ITypeConstraint) pConstraint); - } else if (pConstraint instanceof ITypeInfoProviderConstraint) { - // non-type constraints are all retained - final Set directJudgements = ((ITypeInfoProviderConstraint) pConstraint) - .getImpliedJudgements(context); - subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints, directJudgements, - context); - } - } - Comparator eliminationOrder = (o1, o2) -> { - IInputKey type1 = o1.getEquivalentJudgement().getInputKey(); - IInputKey type2 = o2.getEquivalentJudgement().getInputKey(); - - int result = context.getSuggestedEliminationOrdering().compare(type1, type2); - return (result == 0) - ? PConstraint.COMPARE_BY_MONOTONOUS_ID.compare(o1, o2) - : result; - }; - - Collections.sort(allTypeConstraints, eliminationOrder); - Queue potentialConstraints = allTypeConstraints; // rename for better comprehension - - while (!potentialConstraints.isEmpty()) { - ITypeConstraint candidate = potentialConstraints.poll(); - - boolean isSubsumed = subsumedByRetainedConstraints.contains(candidate.getEquivalentJudgement()); - if (!isSubsumed) { - Set typeClosure = subsumedByRetainedConstraints; - for (ITypeConstraint subsuming : potentialConstraints) { // the remaining ones - final Set directJudgements = subsuming.getImpliedJudgements(context); - typeClosure = TypeHelper.typeClosure(typeClosure, directJudgements, context); - - if (typeClosure.contains(candidate.getEquivalentJudgement())) { - isSubsumed = true; - break; - } - } - } - if (isSubsumed) { // eliminated - candidate.delete(); - derivativeRemoved(candidate, ConstraintRemovalReason.TYPE_SUBSUMED); - } else { // retained - subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints, - candidate.getImpliedJudgements(context), context); - } - } - } - - /** - * Inserts "weakened alternative" constraints suggested by the meta context that aid in coming up with a query plan. - */ - void expandWeakenedAlternativeConstraints(PBody body) { - Set allJudgements = new HashSet(); - Set newJudgementsToAdd = new HashSet(); - Queue judgementsToProcess = new LinkedList(); - Map> traceability = CollectionsFactory.createMap(); - - for (ITypeConstraint typeConstraint : body.getConstraintsOfType(ITypeConstraint.class)) { - TypeJudgement equivalentJudgement = typeConstraint.getEquivalentJudgement(); - judgementsToProcess.add(equivalentJudgement); - allJudgements.add(equivalentJudgement); - traceability.computeIfAbsent(equivalentJudgement, k-> new ArrayList<>()).add(typeConstraint); - } - - while (!judgementsToProcess.isEmpty()) { - TypeJudgement judgement = judgementsToProcess.poll(); - for (TypeJudgement alternativeJudgement : judgement.getWeakenedAlternativeJudgements(context)) { - if (allJudgements.add(alternativeJudgement)) { - newJudgementsToAdd.add(alternativeJudgement); - judgementsToProcess.add(alternativeJudgement); - traceability.merge( - alternativeJudgement, - traceability.getOrDefault(judgement, new ArrayList<>()), - (old,further) -> {old.addAll(further); return old;} - ); - } - } - } - - for (TypeJudgement typeJudgement : newJudgementsToAdd) { - PConstraint newConstraint = typeJudgement.createConstraintFor(body); - for (PConstraint source : traceability.getOrDefault(typeJudgement, Collections.emptyList())) { - addTrace(source, newConstraint); - } - } - } - - private Object getConstraintKey(PConstraint constraint) { - if (constraint instanceof ITypeConstraint) { - return ((ITypeConstraint) constraint).getEquivalentJudgement(); - } - // Do not check duplication for any other types - return constraint; - } - - void eliminateDuplicateTypeConstraints(PBody body) { - Map constraints = new HashMap<>(); - for (PConstraint constraint : body.getConstraints()) { - Object key = getConstraintKey(constraint); - // Retain first found instance of a constraint - if (!constraints.containsKey(key)) { - constraints.put(key, constraint); - } - } - - // Retain collected constraints, remove everything else - Iterator iterator = body.getConstraints().iterator(); - Collection toRetain = constraints.values(); - while(iterator.hasNext()){ - PConstraint next = iterator.next(); - if (!toRetain.contains(next)){ - derivativeRemoved(next, ConstraintRemovalReason.DUPLICATE); - iterator.remove(); - } - } - } - - /** - * Verifies the sanity of all constraints. Should be issued as a preventive check before layouting. - * - * @param body - * @throws RetePatternBuildException - */ - void checkSanity(PBody body) { - for (PConstraint pConstraint : body.getConstraints()) - pConstraint.checkSanity(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java deleted file mode 100644 index c844ccf7..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * An abstract base class for creating alternative representations for PDisjunctions. - * @author Zoltan Ujhelyi - * - */ -public abstract class PDisjunctionRewriter extends AbstractRewriterTraceSource{ - - public abstract PDisjunction rewrite(PDisjunction disjunction); - - public PDisjunction rewrite(PQuery query) { - return rewrite(query.getDisjunctBodies()); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java deleted file mode 100644 index eb5422ca..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.WeakHashMap; - -import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; - -/** - * A rewriter that stores the previously computed results of a rewriter or a rewriter chain. - * - * @author Zoltan Ujhelyi - * @since 1.0 - */ -public class PDisjunctionRewriterCacher extends PDisjunctionRewriter { - - private final List rewriterChain; - private WeakHashMap cachedResults = - new WeakHashMap(); - - private void setupTraceCollectorInChain(){ - IRewriterTraceCollector collector = getTraceCollector(); - for(PDisjunctionRewriter rewriter: rewriterChain){ - rewriter.setTraceCollector(collector); - } - } - - public PDisjunctionRewriterCacher(PDisjunctionRewriter rewriter) { - rewriterChain = Collections.singletonList(rewriter); - } - - public PDisjunctionRewriterCacher(PDisjunctionRewriter... rewriters) { - rewriterChain = new ArrayList<>(Arrays.asList(rewriters)); - } - - public PDisjunctionRewriterCacher(List rewriterChain) { - this.rewriterChain = new ArrayList<>(rewriterChain); - } - - @Override - public PDisjunction rewrite(PDisjunction disjunction) { - if (!cachedResults.containsKey(disjunction)) { - PDisjunction rewritten = disjunction; - setupTraceCollectorInChain(); - for (PDisjunctionRewriter rewriter : rewriterChain) { - rewritten = rewriter.rewrite(rewritten); - } - - cachedResults.put(disjunction, rewritten); - } - return cachedResults.get(disjunction); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java deleted file mode 100644 index 76311d8f..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java +++ /dev/null @@ -1,253 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; -import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter; -import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.ExportedParameterFilter; -import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.HierarchicalName; -import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; -import tools.refinery.viatra.runtime.matchers.util.Sets; - -/** - * This rewriter class holds the query flattening logic - * - * @author Marton Bur - * - */ -public class PQueryFlattener extends PDisjunctionRewriter { - - /** - * Utility function to produce the permutation of every possible mapping of values. - * - * @param values - * @return - */ - private static Set> permutation(Map> values) { - // An ordering of keys is defined here which will help restoring the appropriate values after the execution of - // the cartesian product - List keyList = new ArrayList<>(values.keySet()); - - // Produce list of value sets with the ordering defined by keyList - List> valuesList = new ArrayList>(keyList.size()); - for (K key : keyList) { - valuesList.add(values.get(key)); - } - - // Cartesian product will obey ordering of the list - Set> valueMappings = Sets.cartesianProduct(valuesList); - - // Build result - Set> result = new LinkedHashSet<>(); - for (List valueList : valueMappings) { - Map map = new HashMap<>(); - for (int i = 0; i < keyList.size(); i++) { - map.put(keyList.get(i), valueList.get(i)); - } - result.add(map); - } - - return result; - } - - private IFlattenCallPredicate flattenCallPredicate; - - public PQueryFlattener(IFlattenCallPredicate flattenCallPredicate) { - this.flattenCallPredicate = flattenCallPredicate; - } - - @Override - public PDisjunction rewrite(PDisjunction disjunction) { - PQuery query = disjunction.getQuery(); - - // Check for recursion - Set allReferredQueries = disjunction.getAllReferredQueries(); - for (PQuery referredQuery : allReferredQueries) { - if (referredQuery.getAllReferredQueries().contains(referredQuery)) { - throw new RewriterException("Recursive queries are not supported, can't flatten query named \"{1}\"", - new String[] { query.getFullyQualifiedName() }, "Unsupported recursive query", query); - } - } - - return this.doFlatten(disjunction); - } - - /** - * Return the list of dependencies (including the root) in chronological order - * - * @param rootDisjunction - * @return - */ - private List disjunctionDependencies(PDisjunction rootDisjunction) { - // Disjunctions are first collected into a list usign a depth-first approach, - // which can be iterated backwards while removing duplicates - Deque stack = new ArrayDeque<>(); - LinkedList list = new LinkedList<>(); - stack.push(rootDisjunction); - list.add(rootDisjunction); - - while (!stack.isEmpty()) { - PDisjunction disjunction = stack.pop(); - // Collect dependencies - for (PBody pBody : disjunction.getBodies()) { - for (PConstraint constraint : pBody.getConstraints()) { - if (constraint instanceof PositivePatternCall) { - PositivePatternCall positivePatternCall = (PositivePatternCall) constraint; - if (flattenCallPredicate.shouldFlatten(positivePatternCall)) { - // If the above preconditions meet, the call should be flattened - PDisjunction calledDisjunction = positivePatternCall.getReferredQuery().getDisjunctBodies(); - stack.push(calledDisjunction); - list.add(calledDisjunction); - } - } - } - } - } - - // Remove duplicates (keeping the last instance) and reverse order - Set visited = new HashSet(); - List result = new ArrayList(list.size()); - - list.descendingIterator().forEachRemaining(item -> { - if (!visited.contains(item)) { - result.add(item); - visited.add(item); - } - - }); - - return result; - } - - /** - * This function holds the actual flattening logic for a PQuery - * - * @param rootDisjunction - * to be flattened - * @return the flattened bodies of the pQuery - */ - private PDisjunction doFlatten(PDisjunction rootDisjunction) { - - Map> flatBodyMapping = new HashMap<>(); - - List dependencies = disjunctionDependencies(rootDisjunction); - - for (PDisjunction disjunction : dependencies) { - Set flatBodies = new LinkedHashSet<>(); - for (PBody body : disjunction.getBodies()) { - if (isFlatteningNeeded(body)) { - Map> flattenedBodies = new HashMap<>(); - for (PConstraint pConstraint : body.getConstraints()) { - - if (pConstraint instanceof PositivePatternCall) { - PositivePatternCall positivePatternCall = (PositivePatternCall) pConstraint; - if (flattenCallPredicate.shouldFlatten(positivePatternCall)) { - // If the above preconditions meet, do the flattening and return the disjoint bodies - PDisjunction calledDisjunction = positivePatternCall.getReferredQuery() - .getDisjunctBodies(); - - Set flattenedBodySet = flatBodyMapping.get(calledDisjunction); - Preconditions.checkArgument(!flattenedBodySet.isEmpty()); - flattenedBodies.put(positivePatternCall, flattenedBodySet); - } - } - } - flatBodies.addAll(createSetOfFlatPBodies(body, flattenedBodies)); - } else { - flatBodies.add(prepareFlatPBody(body)); - } - } - flatBodyMapping.put(disjunction, flatBodies); - } - - return new PDisjunction(rootDisjunction.getQuery(), flatBodyMapping.get(rootDisjunction)); - } - - /** - * Creates the flattened bodies based on the caller body and the called (and already flattened) disjunctions - * - * @param pBody - * the body to flatten - * @param flattenedDisjunctions - * the - * @param flattenedCalls - * @return - */ - private Set createSetOfFlatPBodies(PBody pBody, Map> flattenedCalls) { - PQuery pQuery = pBody.getPattern(); - - Set> conjunctedCalls = permutation(flattenedCalls); - - // The result set containing the merged conjuncted bodies - Set conjunctedBodies = new HashSet<>(); - - for (Map calledBodies : conjunctedCalls) { - FlattenerCopier copier = createBodyCopier(pQuery, calledBodies); - - int i = 0; - HierarchicalName hierarchicalNamingTool = new HierarchicalName(); - for (PositivePatternCall patternCall : calledBodies.keySet()) { - // Merge each called body - hierarchicalNamingTool.setCallCount(i++); - copier.mergeBody(patternCall, hierarchicalNamingTool, new ExportedParameterFilter()); - } - - // Merge the caller's constraints to the conjunct body - copier.mergeBody(pBody); - - PBody copiedBody = copier.getCopiedBody(); - copiedBody.setStatus(PQueryStatus.OK); - conjunctedBodies.add(copiedBody); - } - - return conjunctedBodies; - } - - private FlattenerCopier createBodyCopier(PQuery query, Map calledBodies) { - FlattenerCopier flattenerCopier = new FlattenerCopier(query, calledBodies); - flattenerCopier.setTraceCollector(getTraceCollector()); - return flattenerCopier; - } - - private PBody prepareFlatPBody(PBody pBody) { - PBodyCopier copier = createBodyCopier(pBody.getPattern(), Collections. emptyMap()); - copier.mergeBody(pBody, new SameName(), new AllowAllFilter()); - // the copying of the body here is necessary for only one containing PDisjunction can be assigned to a PBody - return copier.getCopiedBody(); - } - - private boolean isFlatteningNeeded(PBody pBody) { - // Check if the body contains positive pattern call AND if it should be flattened - for (PConstraint pConstraint : pBody.getConstraints()) { - if (pConstraint instanceof PositivePatternCall) { - return flattenCallPredicate.shouldFlatten((PositivePatternCall) pConstraint); - } - } - return false; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java deleted file mode 100644 index d0fc286b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import tools.refinery.viatra.runtime.matchers.psystem.queries.QueryInitializationException; - -/** - * An exception to wrap various issues during PDisjunction rewriting. - * @author Zoltan Ujhelyi - * - */ -public class RewriterException extends QueryInitializationException { - - private static final long serialVersionUID = -4703825954995497932L; - - public RewriterException(String message, String[] context, String shortMessage, Object patternDescription, - Throwable cause) { - super(message, context, shortMessage, patternDescription, cause); - } - - public RewriterException(String message, String[] context, String shortMessage, Object patternDescription) { - super(message, context, shortMessage, patternDescription); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java deleted file mode 100644 index 71459558..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java +++ /dev/null @@ -1,63 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import java.util.LinkedHashSet; -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.context.IInputKey; -import tools.refinery.viatra.runtime.matchers.context.surrogate.SurrogateQueryRegistry; -import tools.refinery.viatra.runtime.matchers.psystem.PBody; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; -import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuples; - -/** - * @author Zoltan Ujhelyi - * - */ -public class SurrogateQueryRewriter extends PDisjunctionRewriter { - - @Override - public PDisjunction rewrite(PDisjunction disjunction) { - Set replacedBodies = new LinkedHashSet<>(); - for (PBody body : disjunction.getBodies()) { - PBodyCopier copier = new PBodyCopier(body, getTraceCollector()) { - - @Override - protected void copyTypeConstraint(TypeConstraint typeConstraint) { - PVariable[] mappedVariables = extractMappedVariables(typeConstraint); - Tuple variablesTuple = Tuples.flatTupleOf((Object[])mappedVariables); - final IInputKey supplierKey = typeConstraint.getSupplierKey(); - if(SurrogateQueryRegistry.instance().hasSurrogateQueryFQN(supplierKey)) { - PQuery surrogateQuery = SurrogateQueryRegistry.instance().getSurrogateQuery(supplierKey); - if (surrogateQuery == null) { - throw new IllegalStateException( - String.format("Surrogate query for feature %s not found", - supplierKey.getPrettyPrintableName())); - } - addTrace(typeConstraint, new PositivePatternCall(getCopiedBody(), variablesTuple, surrogateQuery)); - } else { - addTrace(typeConstraint, new TypeConstraint(getCopiedBody(), variablesTuple, supplierKey)); - } - } - }; - PBody modifiedBody = copier.getCopiedBody(); - replacedBodies.add(modifiedBody); - modifiedBody.setStatus(PQueryStatus.OK); - } - return new PDisjunction(replacedBodies); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java deleted file mode 100644 index 10337979..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.psystem.rewriters; - -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.Map; - -import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; -import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; -import tools.refinery.viatra.runtime.matchers.psystem.PVariable; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * A wrapper for {@link IExpressionEvaluator} which is capable of correctly mapping variable names used by the - * expression. - * - * @author Grill Balázs - * - */ -class VariableMappingExpressionEvaluatorWrapper implements IExpressionEvaluator { - - private final IExpressionEvaluator wrapped; - private final Map variableMapping; - - public VariableMappingExpressionEvaluatorWrapper(IExpressionEvaluator wrapped, - Map variableMapping) { - - // Support to rewrap an already wrapped expression. - boolean rewrap = wrapped instanceof VariableMappingExpressionEvaluatorWrapper; - this.wrapped = rewrap ? ((VariableMappingExpressionEvaluatorWrapper)wrapped).wrapped : wrapped; - - // Instead of just saving the reference of the mapping, save the actual (trimmed) state of the mapping as it - // may change during copying (especially during flattening). A LinkedHashMap is used to retain ordering of - // original parameter names iterator. - this.variableMapping = new LinkedHashMap<>(); - - // Index map by variable names - Map names = new HashMap<>(); - for (PVariable originalVar : variableMapping.keySet()) { - names.put(originalVar.getName(), originalVar); - } - - // In case of rewrapping, current names are contained by the previous mapping - Map previousMapping = null; - if (rewrap){ - previousMapping = ((VariableMappingExpressionEvaluatorWrapper)wrapped).variableMapping; - } - - // Populate mapping - for (String inputParameterName : this.wrapped.getInputParameterNames()) { - String parameterName = rewrap ? previousMapping.get(inputParameterName) : inputParameterName; - Preconditions.checkArgument(parameterName != null); - PVariable original = names.get(parameterName); - Preconditions.checkArgument(original != null); - PVariable mapped = variableMapping.get(original); - if (mapped != null){ - this.variableMapping.put(inputParameterName, mapped.getName()); - } - } - } - - @Override - public String getShortDescription() { - return wrapped.getShortDescription(); - } - - @Override - public Iterable getInputParameterNames() { - return variableMapping.values(); - } - - @Override - public Object evaluateExpression(final IValueProvider provider) throws Exception { - return wrapped.evaluateExpression(variableName -> { - String mappedVariableName = variableMapping.get(variableName); - Preconditions.checkArgument(mappedVariableName != null, "Could not find variable %s", variableName); - return provider.getValue(mappedVariableName); - }); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java deleted file mode 100644 index a26d9193..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java +++ /dev/null @@ -1,136 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; - -/** - * Common implementation methods for immutable and volatile tuples. The class should not be used directly in client - * code, except for the definition of new tuple implementations. - * - * @author Zoltan Ujhelyi - * @since 1.7 - */ -public abstract class AbstractTuple implements ITuple { - - /** - * As the tuple is supposed to be immutable, do not modify the returned array. - * - * @return the array containing all elements of this Tuple - */ - @Override - public Object[] getElements() { - Object[] allElements = new Object[getSize()]; - for (int i = 0; i < allElements.length; ++i) - allElements[i] = get(i); - return allElements; - } - - /** - * @return the set containing all distinct elements of this Tuple, cast as type T - */ - @Override - @SuppressWarnings("unchecked") - public Set getDistinctElements() { - Set result = new HashSet(); - Object[] elements = getElements(); - for (Object object : elements) { - result.add((T) object); - } - return result; - } - - /** - * Calculates an inverted index of the elements of this pattern. For each element, the index of the (last) - * occurrence is calculated. - * - * @return the inverted index mapping each element of this pattern to its index in the array - */ - @Override - public Map invertIndex() { - Map result = new HashMap(); - for (int i = 0; i < getSize(); i++) - result.put(get(i), i); - return result; - } - - /** - * Calculates an inverted index of the elements of this pattern. For each element, the index of all of its - * occurrences is calculated. - * - * @return the inverted index mapping each element of this pattern to its index in the array - */ - @Override - public Map> invertIndexWithMupliplicity() { - Map> result = new HashMap>(); - for (int i = 0; i < getSize(); i++) { - Object value = get(i); - List indices = result.computeIfAbsent(value, v -> new ArrayList<>()); - indices.add(i); - } - return result; - } - - /** - * @since 1.7 - */ - protected IndexOutOfBoundsException raiseIndexingError(int index) { - return new IndexOutOfBoundsException( - String.format("No value at position %d for %s instance %s", index, getClass().getSimpleName(), this)); - } - - /** - * Compares the elements stored in this tuple to another tuple - */ - protected boolean internalEquals(ITuple other) { - if (getSize() != other.getSize()) - return false; - boolean result = true; - for (int i = 0; result && i < getSize(); ++i) { - Object ours = get(i); - Object theirs = other.get(i); - result = result && Objects.equals(ours, theirs); - } - return result; - } - - @Override - public String toString() { - StringBuilder s = new StringBuilder(); - s.append("T("); - for (Object o : getElements()) { - s.append(o == null ? "null" : o.toString()); - s.append(';'); - } - s.append(')'); - return s.toString(); - } - - /** - * @since 1.7 - */ - protected int doCalcHash() { - final int PRIME = 31; - int hash = 1; - for (int i = 0; i < getSize(); i++) { - hash = PRIME * hash; - Object element = get(i); - if (element != null) - hash += element.hashCode(); - } - return hash; - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java deleted file mode 100644 index 6f46b763..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java +++ /dev/null @@ -1,20 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -/** - * Base class for all flat tuple implementations. - * Flat tuples store all elements locally (do not reference other tuples). - * - * @author Gabor Bergmann - * @since 1.7 - */ -public abstract class BaseFlatTuple extends Tuple { - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java deleted file mode 100644 index 03f9ea89..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java +++ /dev/null @@ -1,65 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -/** - * Common functionality of left inheritance tuple implementations. - * - *

Left inheritance tuples inherit their first few elements from another tuple, - * and extend it with additional "local" elements. - * - * @author Gabor Bergmann - * @since 1.7 - */ -public abstract class BaseLeftInheritanceTuple extends Tuple { - - /** - * The number of elements that aren't stored locally, but inherited from an ancestor Tuple instead. - */ - protected final int inheritedIndex; - /** - * This object contains the same elements as the ancestor on the first inheritedIndex positions - */ - protected final Tuple ancestor; - - /** - * @param ancestor - */ - public BaseLeftInheritanceTuple(Tuple ancestor) { - super(); - this.ancestor = ancestor; - this.inheritedIndex = ancestor.getSize(); - } - - /** - * @return the number of local (non-inherited) elements - */ - public abstract int getLocalSize(); - - /** - * Optimized equals calculation (prediction: true, since hash values match) - */ - @Override - protected boolean internalEquals(ITuple other) { - if (other instanceof BaseLeftInheritanceTuple) { - BaseLeftInheritanceTuple blit = (BaseLeftInheritanceTuple) other; - if (blit.inheritedIndex == this.inheritedIndex) { - if (this.ancestor.equals(blit.ancestor)) { - return localEquals(blit); - } else return false; - } - } - return super.internalEquals(other); - } - - /** - * Checks the equivalence of local elements only, after ancestor tuple has been determined to be equal. - */ - protected abstract boolean localEquals(BaseLeftInheritanceTuple other); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java deleted file mode 100644 index 8bbb0ac2..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.Arrays; - -/** - * Default Tuple implementation, with statically unknown arity. - * @author Gabor Bergmann - */ -public final class FlatTuple extends BaseFlatTuple { - - /** - * Array of substituted values. DO NOT MODIFY! Use Constructor to build a new instance instead. - */ - private final Object[] elements; - - /** - * Creates a FlatTuple instance, fills it with the given array. - *

Users should consider calling {@link Tuples#flatTupleOf(Object...)} instead to save memory on low-arity tuples. - * - * @param elements - * array of substitution values - */ - protected FlatTuple(Object... elements) { - this.elements = Arrays.copyOf(elements, elements.length); - calcHash(); - } - - @Override - public Object get(int index) { - return elements[index]; - } - - @Override - public int getSize() { - return elements.length; - } - - @Override - public Object[] getElements() { - return elements; - } - - @Override - protected boolean internalEquals(ITuple other) { - if (other instanceof FlatTuple) { - return Arrays.equals(elements, ((FlatTuple) other).elements); - } else - return super.internalEquals(other); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java deleted file mode 100644 index 93f412b7..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -/** - * Flat tuple with statically known arity of 0. - * - * @author Gabor Bergmann - * @since 1.7 - * - */ -public final class FlatTuple0 extends BaseFlatTuple { - protected static final FlatTuple0 INSTANCE = new FlatTuple0(); - - private FlatTuple0() { - calcHash(); - } - - @Override - public int getSize() { - return 0; - } - - @Override - public Object get(int index) { - throw raiseIndexingError(index); - } - - private static final Object[] NULLARY_ARRAY = new Object[0]; - - @Override - public Object[] getElements() { - return NULLARY_ARRAY; - } - - @Override - protected boolean internalEquals(ITuple other) { - return 0 == other.getSize(); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java deleted file mode 100644 index b3b1c312..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.Objects; - -/** - * Flat tuple with statically known arity of 1. - * - * @author Gabor Bergmann - * @since 1.7 - * - */ -public final class FlatTuple1 extends BaseFlatTuple { - private final Object element0; - - protected FlatTuple1(Object element0) { - this.element0 = element0; - calcHash(); - } - - @Override - public int getSize() { - return 1; - } - - @Override - public Object get(int index) { - if (index == 0) return element0; - else throw raiseIndexingError(index); - } - - @Override - protected boolean internalEquals(ITuple other) { - return 1 == other.getSize() && - Objects.equals(element0, other.get(0)); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java deleted file mode 100644 index 2dcfd718..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.Objects; - -/** - * Flat tuple with statically known arity of 2. - * - * @author Gabor Bergmann - * @since 1.7 - * - */ -public final class FlatTuple2 extends BaseFlatTuple { - private final Object element0; - private final Object element1; - - protected FlatTuple2(Object element0, Object element1) { - this.element0 = element0; - this.element1 = element1; - calcHash(); - } - - @Override - public int getSize() { - return 2; - } - - @Override - public Object get(int index) { - switch(index) { - case 0 : return element0; - case 1 : return element1; - default: throw raiseIndexingError(index); - } - } - - @Override - protected boolean internalEquals(ITuple other) { - return 2 == other.getSize() && - Objects.equals(element0, other.get(0)) && - Objects.equals(element1, other.get(1)); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java deleted file mode 100644 index 50cee57e..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.Objects; - -/** - * Flat tuple with statically known arity of 3. - * - * @author Gabor Bergmann - * @since 1.7 - * - */ -public final class FlatTuple3 extends BaseFlatTuple { - private final Object element0; - private final Object element1; - private final Object element2; - - protected FlatTuple3(Object element0, Object element1, Object element2) { - this.element0 = element0; - this.element1 = element1; - this.element2 = element2; - calcHash(); - } - - @Override - public int getSize() { - return 3; - } - - @Override - public Object get(int index) { - switch (index) { - case 0: return element0; - case 1: return element1; - case 2: return element2; - default: throw raiseIndexingError(index); - } - } - - @Override - protected boolean internalEquals(ITuple other) { - return 3 == other.getSize() && - Objects.equals(element0, other.get(0)) && - Objects.equals(element1, other.get(1)) && - Objects.equals(element2, other.get(2)); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java deleted file mode 100644 index 024c94f4..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java +++ /dev/null @@ -1,59 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.Objects; - -/** - * Flat tuple with statically known arity of 4. - * - * @author Gabor Bergmann - * @since 1.7 - * - */ -public final class FlatTuple4 extends BaseFlatTuple { - private final Object element0; - private final Object element1; - private final Object element2; - private final Object element3; - - protected FlatTuple4(Object element0, Object element1, Object element2, Object element3) { - this.element0 = element0; - this.element1 = element1; - this.element2 = element2; - this.element3 = element3; - calcHash(); - } - - @Override - public int getSize() { - return 4; - } - - @Override - public Object get(int index) { - switch(index) { - case 0: return element0; - case 1: return element1; - case 2: return element2; - case 3: return element3; - default: throw raiseIndexingError(index); - } - } - - @Override - protected boolean internalEquals(ITuple other) { - return 4 == other.getSize() && - Objects.equals(element0, other.get(0)) && - Objects.equals(element1, other.get(1)) && - Objects.equals(element2, other.get(2)) && - Objects.equals(element3, other.get(3)); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java deleted file mode 100644 index f5dab2a5..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -/** - * A tuple that allows modifying the underlying value. Should not be used for non-volatile tuples. - * - * @author Zoltan Ujhelyi - * @since 1.7 - */ -public interface IModifiableTuple extends ITuple { - - /** - * Sets the selected value for a tuple - * - * @pre: 0 <= index < getSize() - * - */ - void set(int index, Object value); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java deleted file mode 100644 index 92014781..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Represents both mutable and immutable tuples - * - * @author Zoltan Ujhelyi - * @since 1.7 - * - */ -public interface ITuple { - - /** - * @pre: 0 <= index < getSize() - * - * @return the element at the specified index - */ - Object get(int index); - - /** - * As the tuple is supposed to be immutable, do not modify the returned array. - * @return the array containing all elements of this Tuple - */ - Object[] getElements(); - - /** - * @return the set containing all distinct elements of this Tuple, cast as type T - */ - Set getDistinctElements(); - - /** - * @return number of elements - */ - int getSize(); - - /** - * Calculates an inverted index of the elements of this pattern. For each element, the index of the (last) - * occurrence is calculated. - * - * @return the inverted index mapping each element of this pattern to its index in the array - */ - Map invertIndex(); - - /** - * Calculates an inverted index of the elements of this pattern. For each element, the index of all of its - * occurrences is calculated. - * - * @return the inverted index mapping each element of this pattern to its index in the array - */ - Map> invertIndexWithMupliplicity(); - - Tuple toImmutable(); -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java deleted file mode 100644 index dcdfc376..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java +++ /dev/null @@ -1,172 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.Arrays; -import java.util.Objects; - -/** - * - * Tuple that inherits another tuple on the left. - * - * @author Gabor Bergmann - * - */ -public final class LeftInheritanceTuple extends BaseLeftInheritanceTuple { - /** - * Array of substituted values above inheritedIndex. DO NOT MODIFY! Use Constructor to build a new instance instead. - */ - private final Object[] localElements; - - // - // /** - // * Creates a Tuple instance, fills it with the given array. - // * @pre: no elements are null - // * @param elements array of substitution values - // */ - // public Tuple(Object[] elements) - // { - // this.localElements = elements; - // this.ancestor=null; - // this.inheritedIndex = 0; - // calcHash(); - // } - - - - /** - * Creates a Tuple instance, lets it inherit from an ancestor, extends it with a given array. @pre: no elements are - * null - * - * @param localElements - * array of substitution values - */ - LeftInheritanceTuple(Tuple ancestor, Object[] localElements) { - super(ancestor); - this.localElements = localElements; - calcHash(); - } - - // - // /** - // * Creates a Tuple instance of size one, fills it with the given object. - // * @pre: o!=null - // * @param o the single substitution - // */ - // public Tuple(Object o) - // { - // localElements = new Object [1]; - // localElements[0] = o; - // this.ancestor=null; - // this.inheritedIndex = 0; - // calcHash(); - // } - // - // /** - // * Creates a Tuple instance of size two, fills it with the given objects. - // * @pre: o1!=null, o2!=null - // */ - // public Tuple(Object o1, Object o2) - // { - // localElements = new Object [2]; - // localElements[0] = o1; - // localElements[1] = o2; - // this.ancestor=null; - // this.inheritedIndex = 0; - // calcHash(); - // } - // - // /** - // * Creates a Tuple instance of size three, fills it with the given - // objects. - // * @pre: o1!=null, o2!=null, o3!=null - // */ - // public Tuple(Object o1, Object o2, Object o3) - // { - // localElements = new Object [3]; - // localElements[0] = o1; - // localElements[1] = o2; - // localElements[2] = o3; - // this.ancestor=null; - // this.inheritedIndex = 0; - // calcHash(); - // } - - /** - * @return number of elements - */ - public int getSize() { - return inheritedIndex + localElements.length; - } - - @Override - public int getLocalSize() { - return localElements.length; - } - - /** - * @pre: 0 <= index < getSize() - * - * @return the element at the specified index - */ - public Object get(int index) { - return (index < inheritedIndex) ? ancestor.get(index) : localElements[index - inheritedIndex]; - } - - /** - * Optimized hash calculation - */ - @Override - void calcHash() { - final int PRIME = 31; - cachedHash = ancestor.hashCode(); - for (int i = 0; i < localElements.length; i++) { - cachedHash = PRIME * cachedHash; - Object element = localElements[i]; - if (element != null) - cachedHash += element.hashCode(); - } - } - - /** - * Optimized equals calculation (prediction: true, since hash values match) - */ - @Override - protected boolean localEquals(BaseLeftInheritanceTuple other) { - if (other instanceof LeftInheritanceTuple) { - LeftInheritanceTuple lit = (LeftInheritanceTuple)other; - return Arrays.equals(this.localElements, lit.localElements); - } else { - if (localElements.length != other.getLocalSize()) - return false; - int index = inheritedIndex; - for (int i = 0; i indicesSorted and isNonrepeating may be OPTIONALLY given if known. - * @since 2.0 - */ - protected TupleMask(int[] indices, int sourceWidth, int[] indicesSorted, Boolean isNonrepeating) { - this.indices = indices; - this.sourceWidth = sourceWidth; - this.indicesSorted = indicesSorted; - this.isNonrepeating = isNonrepeating; - } - - /** - * Creates a TupleMask instance that selects given positions. - * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards. - * - *

indicesSorted and isNonrepeating may be OPTIONALLY given if known. - * @since 2.0 - */ - protected static TupleMask fromSelectedIndicesInternal( - int[] selectedIndices, int sourceArity, - int[] indicesSorted, Boolean isNonrepeating) - { - if (selectedIndices.length == 0) // is it nullary? - return new TupleMask0(sourceArity); - - // is it identity? - boolean identity = sourceArity == selectedIndices.length; - if (identity) { - for (int k=0; k < sourceArity; ++k) { - if (selectedIndices[k] != k) { - identity = false; - break; - } - } - } - if (identity) - return new TupleMaskIdentity(selectedIndices, sourceArity); - - // generic case - return new TupleMask(selectedIndices, sourceArity, indicesSorted, isNonrepeating); - } - - /** - * Creates a TupleMask instance that selects given positions in monotonically increasing order. - * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards. - * @since 2.0 - */ - protected static TupleMask fromSelectedMonotonicIndicesInternal(int[] selectedIndices, int sourceArity) - { - return fromSelectedIndicesInternal(selectedIndices, sourceArity, selectedIndices /* also sorted */, true); - } - - /** - * Creates a TupleMask instance of the given size that maps the first 'size' elements intact - */ - public static TupleMask linear(int size, int sourceWidth) { - if (size == sourceWidth) return new TupleMaskIdentity(sourceWidth); - int[] indices = constructLinearSequence(size); - return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); - } - - /** - * An array containing the first {@link size} nonnegative integers in order - * @since 2.0 - */ - protected static int[] constructLinearSequence(int size) { - int[] indices = new int[size]; - for (int i = 0; i < size; i++) - indices[i] = i; - return indices; - } - - /** - * Creates a TupleMask instance of the given size that maps every single element intact - */ - public static TupleMask identity(int size) { - return new TupleMaskIdentity(size); - } - - /** - * Creates a TupleMask instance of the given size that does not emit output. - */ - public static TupleMask empty(int sourceWidth) { - return linear(0, sourceWidth); - } - - /** - * Creates a TupleMask instance that maps the tuple intact save for a single element at the specified index which is - * omitted - */ - public static TupleMask omit(int omission, int sourceWidth) { - int size = sourceWidth - 1; - int[] indices = new int[size]; - for (int i = 0; i < omission; i++) - indices[i] = i; - for (int i = omission; i < size; i++) - indices[i] = i + 1; - return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); - } - - - /** - * Creates a TupleMask instance that selects positions where keep is true - * @since 1.7 - */ - public static TupleMask fromKeepIndicators(boolean[] keep) { - int size = 0; - for (int k = 0; k < keep.length; ++k) - if (keep[k]) - size++; - if (size == keep.length) return new TupleMaskIdentity(size); - int[] indices = new int[size]; - int l = 0; - for (int k = 0; k < keep.length; ++k) - if (keep[k]) - indices[l++] = k; - return fromSelectedMonotonicIndicesInternal(indices, keep.length); - } - - /** - * Creates a TupleMask instance that selects given positions. - * @since 1.7 - */ - public static TupleMask fromSelectedIndices(int sourceArity, Collection selectedIndices) { - int[] selected = integersToIntArray(selectedIndices); - return fromSelectedIndicesInternal(selected, sourceArity, null, null); - } - /** - * Creates a TupleMask instance that selects given positions. - * @since 1.7 - */ - public static TupleMask fromSelectedIndices(int sourceArity, int[] selectedIndices) { - return fromSelectedIndicesInternal(Arrays.copyOf(selectedIndices, selectedIndices.length), sourceArity, null, null); - } - /** - * Creates a TupleMask instance that selects non-null positions of a given tuple - * @since 1.7 - */ - public static TupleMask fromNonNullIndices(ITuple tuple) { - List indices = new ArrayList<>(); - for (int i=0; i < tuple.getSize(); i++) { - if (tuple.get(i) != null) { - indices.add(i); - } - } - if (indices.size() == tuple.getSize()) return new TupleMaskIdentity(indices.size()); - return fromSelectedMonotonicIndicesInternal(integersToIntArray(indices), tuple.getSize()); - } - /** - * @since 1.7 - */ - public static int[] integersToIntArray(Collection selectedIndices) { - int[] selected = new int[selectedIndices.size()]; - int k=0; - for (Integer integer : selectedIndices) { - selected[k++] = integer; - } - return selected; - } - - - /** - * Creates a TupleMask instance that moves an element from one index to other, shifting the others if neccessary. - */ - public static TupleMask displace(int from, int to, int sourceWidth) { - if (from == to) return new TupleMaskIdentity(sourceWidth); - int[] indices = new int[sourceWidth]; - for (int i = 0; i < sourceWidth; i++) - if (i == to) - indices[i] = from; - else if (i >= from && i < to) - indices[i] = i + 1; - else if (i > to && i <= from) - indices[i] = i - 1; - else - indices[i] = i; - return fromSelectedIndicesInternal(indices, sourceWidth, null, true); - } - - /** - * Creates a TupleMask instance that selects a single element of the tuple. - */ - public static TupleMask selectSingle(int selected, int sourceWidth) { - int[] indices = { selected }; - return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); - } - - /** - * Creates a TupleMask instance that selects whatever is selected by left, and appends whatever is selected by - * right. PRE: left and right have the same sourcewidth - */ - public static TupleMask append(TupleMask left, TupleMask right) { - int leftLength = left.indices.length; - int rightLength = right.indices.length; - int[] indices = new int[leftLength + rightLength]; - for (int i = 0; i < leftLength; ++i) - indices[i] = left.indices[i]; - for (int i = 0; i < rightLength; ++i) - indices[i + leftLength] = right.indices[i]; - return fromSelectedIndicesInternal(indices, left.sourceWidth, null, null); - } - - /** - * Generates indicesSorted from indices on demand - */ - void ensureIndicesSorted() { - if (indicesSorted == null) { - indicesSorted = new int[indices.length]; - List list = new LinkedList(); - for (int i = 0; i < indices.length; ++i) - list.add(indices[i]); - java.util.Collections.sort(list); - int i = 0; - for (Integer a : list) - indicesSorted[i++] = a; - } - } - - - - /** - * Returns the first index of the source that is not selected by the mask, or empty if all indices are selected. - *

PRE: mask indices are all different - * @since 2.0 - */ - public OptionalInt getFirstOmittedIndex() { - ensureIndicesSorted(); - int column = 0; - while (column < getSize() && indicesSorted[column] == column) column++; - if (column < getSourceWidth()) return OptionalInt.of(column); - else return OptionalInt.empty(); - } - - - /** - * Returns a selected masked value from the selected tuple. - * @pre: 0 <= index < getSize() - * @since 1.7 - */ - public Object getValue(ITuple original, int index) { - return original.get(indices[index]); - } - - /** - * Sets the selected value in the original tuple based on the mask definition - * - * @pre: 0 <= index < getSize() - * @since 1.7 - */ - public void set(IModifiableTuple tuple, int index, Object value) { - tuple.set(indices[index], value); - } - - /** - * Generates an immutable, masked view of the original tuple. - *

The new tuple will have arity {@link #getSize()}, - * and will consist of the elements of the original tuple, at positions indicated by this mask. - * @since 1.7 - */ - public Tuple transform(ITuple original) { - switch (indices.length) { - case 0: - return FlatTuple0.INSTANCE; - case 1: - return new FlatTuple1(original.get(indices[0])); - case 2: - return new FlatTuple2(original.get(indices[0]), original.get(indices[1])); - case 3: - return new FlatTuple3(original.get(indices[0]), original.get(indices[1]), original.get(indices[2])); - case 4: - return new FlatTuple4(original.get(indices[0]), original.get(indices[1]), original.get(indices[2]), original.get(indices[3])); - default: - Object signature[] = new Object[indices.length]; - for (int i = 0; i < indices.length; ++i) - signature[i] = original.get(indices[i]); - return new FlatTuple(signature); - } - } - - /** - * @return true iff no two selected indices are the same - * @since 2.0 - */ - public boolean isNonrepeating() { - if (isNonrepeating == null) { - ensureIndicesSorted(); - int previous = -1; - int i; - for (i = 0; i < sourceWidth && previous != indicesSorted[i]; ++i) { - previous = indicesSorted[i]; - } - isNonrepeating = (i == sourceWidth); // if not, stopped due to detected repetition - } - return isNonrepeating; - } - - /** - * Returns a tuple `result` that satisfies `this.transform(result).equals(masked)`. Positions of the result tuple - * that are not determined this way will be filled with null. - * - * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true - * @since 1.7 - */ - public Tuple revertFrom(ITuple masked) { - Object[] signature = new Object[sourceWidth]; - for (int i = 0; i < indices.length; ++i) - signature[indices[i]] = masked.get(i); - return Tuples.flatTupleOf(signature); - } - - /** - * Returns a tuple `result`, same arity as the original tuple, that satisfies - * `this.transform(result).equals(this.transform(tuple))`. - * Positions of the result tuple that are not determined this way will be filled with null. - *

In other words, a copy of the original tuple is returned, - * with null substituted at each position that is not selected by this mask. - * - * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true - * @since 2.1 - */ - public Tuple keepSelectedIndices(ITuple original) { - Object[] signature = new Object[sourceWidth]; - for (int i = 0; i < indices.length; ++i) - signature[indices[i]] = original.get(indices[i]); - return Tuples.flatTupleOf(signature); - } - - /** - * Generates an immutable, masked view of the original tuple. - *

The list will have arity {@link #getSize()}, - * and will consist of the elements of the original tuple, at positions indicated by this mask. - */ - public List transform(List original) { - List signature = new ArrayList(indices.length); - for (int i = 0; i < indices.length; ++i) - signature.add(original.get(indices[i])); - return signature; - } - - /** - * Transforms a given mask directly, instead of transforming tuples that were transformed by the other mask. - * - * @return a mask that cascades the effects this mask after the mask provided as parameter. - */ - public TupleMask transform(TupleMask mask) { - int[] cascadeIndices = new int[indices.length]; - for (int i = 0; i < indices.length; ++i) - cascadeIndices[i] = mask.indices[indices[i]]; - return fromSelectedIndicesInternal(cascadeIndices, mask.sourceWidth, null, null); - } - - // /** - // * Generates a complementer mask that maps those elements that were - // untouched by the original mask. - // * Ordering is left intact. - // * A Tuple is used for reference concerning possible equalities among - // elements. - // */ - // public TupleMask complementer(Tuple reference) - // { - // HashSet touched = new HashSet(); - // LinkedList untouched = new LinkedList(); - // - // for (int index : indices) touched.add(reference.get(index)); - // for (int index=0; index"); - for (int i : indices) { - s.append(i); - s.append(','); - } - s.append(')'); - return s.toString(); - } - - /** - * Returns the size of the masked tuples described by this mask - * @since 1.7 - */ - public int getSize() { - return indices.length; - } - - /** - * Returns the size of the original tuples handled by this mask - * @since 1.7 - */ - public int getSourceWidth() { - return sourceWidth; - } - - - /** - * @return true iff this mask is a no-op - * @since 2.0 - */ - public boolean isIdentity() { - // Contract: if identity mask, a specialized subclass is constructed instead - return false; - } - - /** - * Transforms the given list by applying the mask and putting all results into a set; keeping only a single element - * in case of the mapping result in duplicate values. - * - * @since 1.7 - */ - public Set transformUnique(List original) { - Set signature = new HashSet<>(); - for (int i = 0; i < indices.length; ++i) - signature.add(original.get(indices[i])); - return signature; - } - - /** - * @return the list of selected indices - * @since 2.1 - */ - public List getIndicesAsList() { - List result = new ArrayList(indices.length); - for (int i = 0; i < indices.length; ++i) - result.add(indices[i]); - return result; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java deleted file mode 100644 index 5a0c79ff..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.Collections; -import java.util.List; - -/** - * @author Gabor Bergmann - * @since 1.7 - */ -public final class TupleMask0 extends TupleMask { - - private final static int[] EMPTY_ARRAY = {}; - - /** - * PRE: indices.length == 0 - */ - TupleMask0(int sourceWidth) { - super(EMPTY_ARRAY, sourceWidth, EMPTY_ARRAY, true); - } - - @Override - public List transform(List original) { - return Collections.emptyList(); - } - - @Override - public Tuple transform(ITuple original) { - return Tuples.staticArityFlatTupleOf(); - } - - @Override - public TupleMask transform(TupleMask mask) { - return new TupleMask0(mask.sourceWidth); - } - - @Override - public Tuple combine(Tuple unmasked, Tuple masked, boolean useInheritance, boolean asComplementer) { - if (asComplementer) - return unmasked; - else - return super.combine(unmasked, masked, useInheritance, asComplementer); - } - - @Override - public boolean isIdentity() { - return 0 == sourceWidth; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java deleted file mode 100644 index 62746587..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java +++ /dev/null @@ -1,51 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.List; - -/** - * @author Gabor Bergmann - * @since 1.7 - */ -public final class TupleMaskIdentity extends TupleMask { - - TupleMaskIdentity(int sourceWidth) { - this(constructLinearSequence(sourceWidth), sourceWidth); - } - TupleMaskIdentity(int[] indices, int sourceWidth) { - super(indices, sourceWidth, indices, true); - } - - @Override - public List transform(List original) { - return original; - } - - @Override - public Tuple transform(ITuple original) { - return original.toImmutable(); - } - - @Override - public TupleMask transform(TupleMask mask) { - return mask; - } - - @Override - public Tuple revertFrom(ITuple masked) { - return masked.toImmutable(); - } - - @Override - public boolean isIdentity() { - return true; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java deleted file mode 100644 index 79193516..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import java.util.Map; - -import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; - -/** - * @author Zoltan Ujhelyi - * @since 1.7 - */ -public class TupleValueProvider implements IValueProvider { - - final ITuple tuple; - final Map indexMapping; - - /** - * Wraps a tuple with an index mapping - * @param tuple - * @param indexMapping - */ - public TupleValueProvider(ITuple tuple, Map indexMapping) { - super(); - this.tuple = tuple; - this.indexMapping = indexMapping; - } - - @Override - public Object getValue(String variableName) { - Integer index = indexMapping.get(variableName); - if (index == null) { - throw new IllegalArgumentException(String.format("Variable %s is not present in mapping.", variableName)); - } - Object value = tuple.get(index); - if (value == null) { - throw new IllegalArgumentException(String.format("Variable %s is not found using index %d.", variableName, index)); - } - return value; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java deleted file mode 100644 index 5e41d7d8..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java +++ /dev/null @@ -1,157 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -/** - * Common static factory utilities for tuples. - * - * @author Gabor Bergmann - * @since 1.7 - */ -public class Tuples { - - private Tuples() { - // Empty utility class constructor - } - - /** - * Creates a flat tuple consisting of the given elements. - * For low-arity tuples, specialized implementations - * (such as {@link FlatTuple2}) will be instantiated. - * - *

In case the exact arity is statically known, - * it may be more efficient for the client to instantiate - * the appropriate specialized implementation - * (via {@link #staticArityFlatTupleOf(Object, Object)} etc. - * or {@link #wideFlatTupleOf(Object...)}), - * instead of invoking this method. - * This method does a runtime arity check, and therefore - * also appropriate if the arity is determined at runtime. - */ - public static Tuple flatTupleOf(Object... elements) { - switch (elements.length) { - case 0: - return FlatTuple0.INSTANCE; - case 1: - return new FlatTuple1(elements[0]); - case 2: - return new FlatTuple2(elements[0], elements[1]); - case 3: - return new FlatTuple3(elements[0], elements[1], elements[2]); - case 4: - return new FlatTuple4(elements[0], elements[1], elements[2], elements[3]); - default: - return new FlatTuple(elements); - } - } - /** - * Creates a left inheritance tuple that extends an ancestor tuple - * by the given "local" elements. - * For locally low-arity tuples, specialized implementations - * (such as {@link LeftInheritanceTuple2}) will be instantiated. - * - *

In case the exact arity is statically known, - * it may be more efficient for the client to instantiate - * the appropriate specialized implementation - * (via {@link #staticArityLeftInheritanceTupleOf(Object, Object)} etc. - * or {@link #wideLeftInheritanceTupleOf(Object...)}), - * instead of invoking this method. - * This method does a runtime arity check, and therefore - * also appropriate if the arity is determined at runtime. - */ - public static Tuple leftInheritanceTupleOf(Tuple ancestor, Object... localElements) { - switch (localElements.length) { - case 0: - return ancestor; - case 1: - return new LeftInheritanceTuple1(ancestor, localElements[0]); - case 2: - return new LeftInheritanceTuple2(ancestor, localElements[0], localElements[1]); - case 3: - return new LeftInheritanceTuple3(ancestor, localElements[0], localElements[1], localElements[2]); - case 4: - return new LeftInheritanceTuple4(ancestor, localElements[0], localElements[1], localElements[2], localElements[3]); - default: - return new LeftInheritanceTuple(ancestor, localElements); - } - } - - /** - * Creates a flat tuple consisting of no elements. - */ - public static Tuple staticArityFlatTupleOf() { - return FlatTuple0.INSTANCE; - } - /** - * Creates a flat tuple consisting of the given single element. - */ - public static Tuple staticArityFlatTupleOf(Object element) { - return new FlatTuple1(element); - } - /** - * Creates a flat tuple consisting of the given elements. - */ - public static Tuple staticArityFlatTupleOf(Object element0, Object element1) { - return new FlatTuple2(element0, element1); - } - /** - * Creates a flat tuple consisting of the given elements. - */ - public static Tuple staticArityFlatTupleOf(Object element0, Object element1, Object element2) { - return new FlatTuple3(element0, element1, element2); - } - /** - * Creates a flat tuple consisting of the given elements. - */ - public static Tuple staticArityFlatTupleOf(Object element0, Object element1, Object element2, Object element3) { - return new FlatTuple4(element0, element1, element2, element3); - } - /** - * Creates a flat tuple consisting of the given elements. - *

Invoke this only if it is statically known that the tuple will be wide. - * Otherwise, use {@link #flatTupleOf(Object...)}. - */ - public static Tuple wideFlatTupleOf(Object... elements) { - return new FlatTuple(elements); - } - - /** - * Creates a left inheritance tuple consisting of the given single local element. - */ - public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element) { - return new LeftInheritanceTuple1(ancestor, element); - } - /** - * Creates a left inheritance tuple consisting of the given local elements. - */ - public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1) { - return new LeftInheritanceTuple2(ancestor, element0, element1); - } - /** - * Creates a left inheritance tuple consisting of the given local elements. - */ - public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1, Object element2) { - return new LeftInheritanceTuple3(ancestor, element0, element1, element2); - } - /** - * Creates a left inheritance tuple consisting of the given local elements. - */ - public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1, Object element2, Object element3) { - return new LeftInheritanceTuple4(ancestor, element0, element1, element2, element3); - } - /** - * Creates a left inheritance tuple consisting of the given local elements. - *

Invoke this only if it is statically known that the tuple will be wide. - * Otherwise, use {@link #leftInheritanceTupleOf(Tuple, Object...)}. - */ - public static Tuple wideLeftInheritanceTupleOf(Tuple ancestor, Object... elements) { - return new LeftInheritanceTuple(ancestor, elements); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java deleted file mode 100644 index f683d544..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * This class provides a volatile tuple view with a given mask of a given tuple instance. If the masked tuple changes, - * the view updates as well. - * - * @author Zoltan Ujhelyi - * @since 1.7 - * - */ -public class VolatileMaskedTuple extends VolatileTuple { - - protected final TupleMask mask; - protected ITuple source; - - public VolatileMaskedTuple(ITuple source, TupleMask mask) { - this.source = source; - this.mask = mask; - } - - public VolatileMaskedTuple(TupleMask mask) { - this(null, mask); - } - - public void updateTuple(ITuple newSource) { - source = newSource; - } - - @Override - public Object get(int index) { - Preconditions.checkState(source != null, "Source tuple is not set."); - return mask.getValue(source, index); - } - - @Override - public int getSize() { - return mask.getSize(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java deleted file mode 100644 index 92306c6e..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.tuple; - -import tools.refinery.viatra.runtime.matchers.util.Preconditions; - -/** - * A masked tuple implementation that allows modifying the backing tuple. - * @author Zoltan Ujhelyi - * @since 1.7 - * - */ -public class VolatileModifiableMaskedTuple extends VolatileMaskedTuple implements IModifiableTuple { - - private IModifiableTuple modifiableTuple; - - public VolatileModifiableMaskedTuple(IModifiableTuple source, TupleMask mask) { - super(source, mask); - modifiableTuple = source; - } - - public VolatileModifiableMaskedTuple(TupleMask mask) { - this(null, mask); - } - - @Override - public void updateTuple(ITuple newSource) { - Preconditions.checkArgument(newSource instanceof IModifiableTuple, "Provided tuple does not support updates"); - this.updateTuple((IModifiableTuple)newSource); - } - - public void updateTuple(IModifiableTuple newSource) { - super.updateTuple(newSource); - modifiableTuple = newSource; - } - - @Override - public void set(int index, Object value) { - mask.set(modifiableTuple, index, value); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java deleted file mode 100644 index 699105a5..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017 Zoltan Ujhelyi, IncQuery Labs - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.tuple; - -/** - * Mutable tuple without explicit modification commands. In practical terms, the values stored in a volatile tuple can - * be changed without any notification. - * - * @author Zoltan Ujhelyi - * @since 1.7 - * - */ -public abstract class VolatileTuple extends AbstractTuple { - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof ITuple)) - return false; - final ITuple other = (ITuple) obj; - return internalEquals(other); - } - - @Override - public int hashCode() { - return doCalcHash(); - } - - /** - * Creates an immutable tuple from the values stored in the tuple. The created tuple will not be updated when the - * current tuple changes. - */ - @Override - public Tuple toImmutable() { - return Tuples.flatTupleOf(getElements()); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java deleted file mode 100644 index 338990ab..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -/** - * The degree of accuracy of a cardinality estimate - * @author Gabor Bergmann - * @since 2.1 - */ -public enum Accuracy { - EXACT_COUNT, - BEST_UPPER_BOUND, - BEST_LOWER_BOUND, - APPROXIMATION; - - /** - * Partial order comparison. - */ - public boolean atLeastAsPreciseAs(Accuracy other) { - switch (this) { - case EXACT_COUNT: return true; - case APPROXIMATION: return APPROXIMATION == other; - case BEST_UPPER_BOUND: return BEST_UPPER_BOUND == other || APPROXIMATION == other; - case BEST_LOWER_BOUND: return BEST_LOWER_BOUND == other || APPROXIMATION == other; - default: throw new IllegalArgumentException(); - } - } - - /** - * @return another accuracy value that is anti-monotonic to this one, - * i.e. an accuracy that should be used in the denominator to obtain a fraction with this accuracy - */ - public Accuracy reciprocal() { - switch(this) { - case APPROXIMATION: return APPROXIMATION; - case BEST_UPPER_BOUND: return BEST_LOWER_BOUND; - case BEST_LOWER_BOUND: return BEST_UPPER_BOUND; - case EXACT_COUNT: return EXACT_COUNT; - default: throw new IllegalArgumentException(); - } - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java deleted file mode 100644 index 1b09aec6..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java +++ /dev/null @@ -1,23 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.util; - -/** - * @author Gabor Bergmann - * @since 1.7 - * An instance of clearable pattern memory. - */ -public interface Clearable { - /** - * Clear all partial matchings stored in memory - * - */ - void clear(); -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java deleted file mode 100644 index 590a1ec3..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java +++ /dev/null @@ -1,188 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.function.Function; - -/** - * Factory class used as an accessor to Collections implementations. - * @author istvanrath - */ -public final class CollectionsFactory -{ - - /** - * Instantiates a new empty map. - * @since 1.7 - */ - public static Map createMap() { - return FRAMEWORK.createMap(); - } - - /** - * Instantiates a new map with the given initial contents. - * @since 1.7 - */ - public static Map createMap(Map initial) { - return FRAMEWORK.createMap(initial); - } - - /** - * Instantiates a new tree map. - * @since 2.3 - */ - public static TreeMap createTreeMap() { - return FRAMEWORK.createTreeMap(); - } - - /** - * Instantiates a new empty set. - * @since 1.7 - */ - public static Set createSet() { - return FRAMEWORK.createSet(); - } - - /** - * Instantiates a new set with the given initial contents. - * @since 1.7 - */ - public static Set createSet(Collection initial) { - return FRAMEWORK.createSet(initial); - } - - /** - * Instantiates an empty set; the key parameter is used to allow using this as a method reference as a - * {@link Function}, e.g. in {@link Map#computeIfAbsent(Object, Function)}. - * - * @param key - * the value of this parameter is ignored - * @since 2.0 - */ - public static Set emptySet(Object key) { - return FRAMEWORK.createSet(); - } - - /** - * Instantiates a new empty multiset. - * @since 1.7 - */ - public static IMultiset createMultiset() { - return FRAMEWORK.createMultiset(); - } - - /** - * Instantiates an empty multiset; the key parameter is used to allow using this as a method reference as a - * {@link Function}, e.g. in {@link Map#computeIfAbsent(Object, Function)}. - * - * @param key - * the value of this parameter is ignored - * @since 2.0 - */ - public static IMultiset emptyMultiset(Object key) { - return FRAMEWORK.createMultiset(); - } - - /** - * Instantiates a new empty delta bag. - * @since 1.7 - */ - public static IDeltaBag createDeltaBag() { - return FRAMEWORK.createDeltaBag(); - } - - /** - * Instantiates a new list that is optimized for registering observers / callbacks. - * @since 1.7 - */ - public static List createObserverList() { - return FRAMEWORK.createObserverList(); - } - - /** - * Instantiates a size-optimized multimap from keys to sets of values. - *

For a single key, many values can be associated according to the given bucket semantics. - *

The keys and values are stored as type fromKeys resp. ofValues; - * currently Object.class and Long.class are supported. - * @since 2.0 - */ - public static IMultiLookup createMultiLookup( - Class fromKeys, MemoryType toBuckets, Class ofValues) { - return FRAMEWORK.createMultiLookup(fromKeys, toBuckets, ofValues); - } - - /** - * Instantiates a memory storing values. - *

For a single key, many values can be associated according to the given memory semantics. - *

The values are stored as type 'values'; - * currently Object.class and Long.class are supported. - * @since 2.0 - */ - public static IMemory createMemory( - Class values, MemoryType memoryType) { - return FRAMEWORK.createMemory(values, memoryType); - } - - /** - * The type of {@link IMemory} - * @since 2.0 - * TODO add delta as type - */ - public enum MemoryType { - /** - * A single key-value pair is stored at most once - */ - SETS, - /** - * Duplicate key-value pairs allowed - */ - MULTISETS - } - - /** - * The collections framework of the current configuration. - * @since 1.7 - */ - private static final ICollectionsFramework FRAMEWORK = new EclipseCollectionsFactory(); - - /** - * Interface abstracting over a collections technology that provides custom collection implementations. - * @since 1.7 - */ - public static interface ICollectionsFramework { - - public abstract Map createMap(); - public abstract Map createMap(Map initial); - /** - * @since 2.3 - */ - public abstract TreeMap createTreeMap(); - public abstract Set createSet(); - public abstract Set createSet(Collection initial); - public abstract IMultiset createMultiset(); - public abstract IDeltaBag createDeltaBag(); - public abstract List createObserverList(); - - /** - * @since 2.0 - */ - public abstract IMultiLookup createMultiLookup( - Class fromKeys, MemoryType toBuckets, Class ofValues); - /** - * @since 2.0 - */ - public abstract IMemory createMemory(Class values, MemoryType memoryType); - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java deleted file mode 100644 index 88f7ec00..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -/** - * Indicates whether a propagated update event signals the insertion or deletion of an element - * - * @author Gabor Bergmann - */ -public enum Direction { - INSERT, DELETE; - - /** - * @since 2.4 - */ - public Direction opposite() { - switch (this) { - case INSERT: - return DELETE; - default: - return INSERT; - } - } - - /** - * @since 2.4 - */ - public char asSign() { - switch (this) { - case INSERT: - return '+'; - default: - return '-'; - } - } - - /** - * Returns the direction that is the product of this direction and the other direction. - * - * DELETE x DELETE = INSERT - * DELETE x INSERT = DELETE - * INSERT x DELETE = DELETE - * INSERT x INSERT = INSERT - * @since 2.4 - */ - public Direction multiply(final Direction other) { - switch (this) { - case DELETE: - return other.opposite(); - default: - return other; - } - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java deleted file mode 100644 index e24b2448..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java +++ /dev/null @@ -1,86 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Iterator; -import java.util.Set; -import java.util.function.BiConsumer; - -import org.eclipse.collections.impl.map.mutable.primitive.ObjectIntHashMap; - -/** - * Eclipse Collections-based multiset for tuples. Can contain duplicate occurrences of the same matching. - * - *

Inherits Eclipse Collections' Object-to-Int primitive hashmap and counts the number of occurrences of each value. - * Element is deleted if # of occurences drops to 0. - * - * @author Gabor Bergmann. - * @since 1.7 - * @noreference - */ -public abstract class EclipseCollectionsBagMemory extends ObjectIntHashMap implements IMemory { - - public EclipseCollectionsBagMemory() { - super(); - } - - @Override - public int getCount(T value) { - return super.getIfAbsent(value, 0); - } - @Override - public int getCountUnsafe(Object value) { - return super.getIfAbsent(value, 0); - } - @Override - public boolean containsNonZero(T value) { - return super.containsKey(value); - } - @Override - public boolean containsNonZeroUnsafe(Object value) { - return super.containsKey(value); - } - - @Override - public void clearAllOf(T value) { - super.remove(value); - } - - - @Override - public Iterator iterator() { - return super.keySet().iterator(); - } - - @Override - public String toString() { - return "TM" + super.toString(); - } - - @Override - public Set distinctValues() { - return super.keySet(); - } - - @Override - public void forEachEntryWithMultiplicities(BiConsumer entryConsumer) { - super.forEachKeyValue(entryConsumer::accept); - } - - @Override - public int hashCode() { - return IMemoryView.hashCode(this); - } - @Override - public boolean equals(Object obj) { - return IMemoryView.equals(this, obj); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java deleted file mode 100644 index 94ec33cd..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java +++ /dev/null @@ -1,41 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -/** - * @author Gabor Bergmann - * @since 1.7 - */ -public class EclipseCollectionsDeltaBag extends EclipseCollectionsBagMemory implements IDeltaBag { - - @Override - public boolean addOne(T value) { - return addSigned(value, +1); - } - - @Override - public boolean addSigned(T value, int count) { - int oldCount = super.getIfAbsent(value, 0); - int newCount = oldCount + count; - - boolean becomesZero = newCount == 0; - if (becomesZero) - super.removeKey(value); - else - super.put(value, newCount); - - return becomesZero || oldCount == 0; - } - - - @Override - public boolean removeOne(T value) { - return addSigned(value, -1); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java deleted file mode 100644 index 5a623c9b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java +++ /dev/null @@ -1,159 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; - -import org.eclipse.collections.api.map.MutableMap; -import org.eclipse.collections.impl.factory.Maps; -import org.eclipse.collections.impl.factory.Sets; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.ICollectionsFramework; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; - -/** - * @author Gabor Bergmann - * @since 1.7 - * @noreference This class is not intended to be referenced by clients. - */ -public class EclipseCollectionsFactory implements ICollectionsFramework { - - @Override - public Map createMap() { - return Maps.mutable.empty(); - } - - @Override - public Map createMap(Map initial) { - MutableMap result = Maps.mutable.ofInitialCapacity(initial.size()); - result.putAll(initial); - return result; - } - - @Override - public TreeMap createTreeMap() { - // eclipse collections is doing the same - return new TreeMap<>(); - } - - @Override - public Set createSet() { - return Sets.mutable.empty(); - } - - @Override - public Set createSet(Collection initial) { - return Sets.mutable.ofAll(initial); - } - - @Override - public IMultiset createMultiset() { - return new EclipseCollectionsMultiset(); - } - - @Override - public IDeltaBag createDeltaBag() { - return new EclipseCollectionsDeltaBag(); - } - - @Override - public List createObserverList() { - return new ArrayList(1); // keep concurrent modification exceptions for error detection - // Lists.mutable.empty - - } - - @Override - @SuppressWarnings({ "unchecked", "rawtypes" }) - public IMultiLookup createMultiLookup( - Class fromKeys, - MemoryType toBuckets, - Class ofValues) - { - boolean longKeys = Long.class.equals(fromKeys); - boolean objectKeys = Object.class.equals(fromKeys); - if (! (longKeys || objectKeys)) throw new IllegalArgumentException(fromKeys.getName()); - boolean longValues = Long.class.equals(ofValues); - boolean objectValues = Object.class.equals(ofValues); - if (! (longValues || objectValues)) throw new IllegalArgumentException(ofValues.getName()); - - if (longKeys) { // K == java.lang.Long - if (longValues) { // V == java.lang.Long - switch(toBuckets) { - case MULTISETS: - return (IMultiLookup) new EclipseCollectionsMultiLookup.FromLongs.ToMultisets.OfLongs(); - case SETS: - return (IMultiLookup) new EclipseCollectionsMultiLookup.FromLongs.ToSets.OfLongs(); - default: - throw new IllegalArgumentException(toBuckets.toString()); - } - } else { // objectValues - switch(toBuckets) { - case MULTISETS: - return new EclipseCollectionsMultiLookup.FromLongs.ToMultisets.OfObjects(); - case SETS: - return new EclipseCollectionsMultiLookup.FromLongs.ToSets.OfObjects(); - default: - throw new IllegalArgumentException(toBuckets.toString()); - } - } - } else { // objectKeys - if (longValues) { // V == java.lang.Long - switch(toBuckets) { - case MULTISETS: - return new EclipseCollectionsMultiLookup.FromObjects.ToMultisets.OfLongs(); - case SETS: - return new EclipseCollectionsMultiLookup.FromObjects.ToSets.OfLongs(); - default: - throw new IllegalArgumentException(toBuckets.toString()); - } - } else { // objectValues - switch(toBuckets) { - case MULTISETS: - return new EclipseCollectionsMultiLookup.FromObjects.ToMultisets.OfObjects(); - case SETS: - return new EclipseCollectionsMultiLookup.FromObjects.ToSets.OfObjects(); - default: - throw new IllegalArgumentException(toBuckets.toString()); - } - } - } - } - - @Override - @SuppressWarnings("unchecked") - public IMemory createMemory(Class values, MemoryType memoryType) { - if (Long.class.equals(values)) { // T == java.lang.Long - switch(memoryType) { - case MULTISETS: - return (IMemory) new EclipseCollectionsLongMultiset(); - case SETS: - return (IMemory) new EclipseCollectionsLongSetMemory(); - default: - throw new IllegalArgumentException(memoryType.toString()); - } - } else { // objectValues - switch(memoryType) { - case MULTISETS: - return new EclipseCollectionsMultiset<>(); - case SETS: - return new EclipseCollectionsSetMemory<>(); - default: - throw new IllegalArgumentException(memoryType.toString()); - } - } - } - - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java deleted file mode 100644 index 88773c5d..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java +++ /dev/null @@ -1,150 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Iterator; -import java.util.Set; -import java.util.function.BiConsumer; - -import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; - -/** - * @author Gabor Bergmann - * @since 2.0 - *

TODO refactor common methods with {@link EclipseCollectionsMultiset} - *

TODO refactor into LongBagMemory etc. - */ -public class EclipseCollectionsLongMultiset extends LongIntHashMap implements IMultiset { - - @Override - public boolean addOne(Long value) { - int oldCount = super.getIfAbsent(value, 0); - - super.put(value, oldCount + 1); - - return oldCount == 0; - } - - @Override - public boolean addSigned(Long value, int count) { - int oldCount = super.getIfAbsent(value, 0); - int newCount = oldCount + count; - - boolean becomesZero = newCount == 0; - if (newCount < 0) - throw new IllegalStateException(String.format( - "Cannot remove %d occurrences of value '%s' as only %d would remain in %s", - count, value, newCount, this)); - else if (becomesZero) - super.removeKey(value); - else // (newCount > 0) - super.put(value, newCount); - - return becomesZero || oldCount == 0; - } - - @Override - public boolean removeOne(Long value) { - return removeOneInternal(value, true); - } - /** - * @since 2.3 - */ - @Override - public boolean removeOneOrNop(Long value) { - return removeOneInternal(value, false); - } - - - /** - * @since 2.3 - */ - protected boolean removeOneInternal(Long value, boolean throwIfImpossible) { - int oldCount = super.getIfAbsent(value, 0); - if (oldCount == 0) { - if (throwIfImpossible) throw new IllegalStateException(String.format( - "Cannot remove value '%s' that is not contained in %s", - value, this)); - else return false; - } - - int rest = oldCount - 1; - boolean empty = rest == 0; - - if (!empty) { - super.put(value, rest); - } else { - super.remove(value); - } - - return empty; - } - - @Override - public void clearAllOf(Long value) { - super.remove(value); - } - - @Override - public int getCount(Long value) { - return super.getIfAbsent(value, 0); - } - @Override - public int getCountUnsafe(Object value) { - return value instanceof Long ? getCount((Long) value) : 0; - } - - @Override - public boolean containsNonZero(Long value) { - return super.containsKey(value); - } - - @Override - public boolean containsNonZeroUnsafe(Object value) { - return value instanceof Long && containsNonZero((Long) value); - } - - @Override - public Iterator iterator() { - return EclipseCollectionsLongSetMemory.iteratorOf(super.keySet()); - } - - @Override - public boolean addPositive(Long value, int count) { - if (count < 0) { - throw new IllegalArgumentException("The count value must be positive!"); - } - - int oldCount = super.getIfAbsent(value, 0); - - super.put(value, oldCount + count); - - return oldCount == 0; - } - - @Override - public Set distinctValues() { - return new EclipseCollectionsLongSetMemory.SetWrapper(super.keySet()); - } - - @Override - public void forEachEntryWithMultiplicities(BiConsumer entryConsumer) { - super.forEachKeyValue(entryConsumer::accept); - } - - @Override - public int hashCode() { - return IMemoryView.hashCode(this); - } - @Override - public boolean equals(Object obj) { - return IMemoryView.equals(this, obj); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java deleted file mode 100644 index fceb54fc..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java +++ /dev/null @@ -1,212 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; - -import org.eclipse.collections.api.LongIterable; -import org.eclipse.collections.api.iterator.LongIterator; -import org.eclipse.collections.api.set.primitive.LongSet; -import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; - -/** - * @author Gabor Bergmann - * @since 2.0 - */ -public class EclipseCollectionsLongSetMemory extends LongHashSet implements ISetMemory { - - @Override - public boolean addOne(Long value) { - return super.add(value); - } - - @Override - public boolean addSigned(Long value, int count) { - if (count == 1) return addOne(value); - else if (count == -1) return removeOne(value); - else throw new IllegalStateException(); - } - - @Override - public boolean removeOne(Long value) { - // Kept for binary compatibility - return ISetMemory.super.removeOne(value); - } - - /** - * @since 2.3 - */ - @Override - public boolean removeOneOrNop(Long value) { - return super.remove(value); - } - - @Override - public void clearAllOf(Long value) { - super.remove(value); - } - - @Override - public int getCount(Long value) { - return super.contains(value) ? 1 : 0; - } - - @Override - public int getCountUnsafe(Object value) { - return value instanceof Long ? getCount((Long) value) : 0; - } - - @Override - public boolean containsNonZero(Long value) { - return super.contains(value); - } - - @Override - public boolean containsNonZeroUnsafe(Object value) { - return value instanceof Long && containsNonZero((Long) value); - } - - @Override - public Iterator iterator() { - return iteratorOf(this); - } - - @Override - public Set distinctValues() { - return new SetWrapper(this); - } - - @Override - public boolean isEmpty() { - return super.isEmpty(); - } - - /** - * Helper for iterating a LongIterable - */ - public static Iterator iteratorOf(LongIterable wrapped) { - return new Iterator() { - LongIterator longIterator = wrapped.longIterator(); - - @Override - public boolean hasNext() { - return longIterator.hasNext(); - } - - @Override - public Long next() { - return longIterator.next(); - } - }; - } - - @Override - public int hashCode() { - return IMemoryView.hashCode(this); - } - @Override - public boolean equals(Object obj) { - return IMemoryView.equals(this, obj); - } - - - /** - * Helper that presents a primitive collection as a Set view - * @author Gabor Bergmann - */ - public static final class SetWrapper implements Set { - private LongSet wrapped; - - /** - * @param wrapped - */ - public SetWrapper(LongSet wrapped) { - this.wrapped = wrapped; - } - - @Override - public int size() { - return wrapped.size(); - } - - @Override - public boolean isEmpty() { - return wrapped.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return o instanceof Long && wrapped.contains((Long)o); - } - - @Override - public Iterator iterator() { - return iteratorOf(wrapped); - } - - @Override - public boolean containsAll(Collection c) { - for (Object object : c) { - if (contains(object)) - return true; - } - return false; - } - - @Override - public Object[] toArray() { - return toArray(new Long[wrapped.size()]); - } - - @Override - @SuppressWarnings("unchecked") - public T[] toArray(T[] a) { - int k = 0; - LongIterator iterator = wrapped.longIterator(); - while (iterator.hasNext()) - a[k++] = (T) Long.valueOf(iterator.next()); - return a; - } - - @Override - public boolean add(Long e) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean remove(Object o) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean addAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean retainAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public boolean removeAll(Collection c) { - throw new UnsupportedOperationException(); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(); - } - - - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java deleted file mode 100644 index 394135c9..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java +++ /dev/null @@ -1,226 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import org.eclipse.collections.impl.map.mutable.UnifiedMap; -import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; -import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedMultiset; -import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet; - -import java.util.Set; -import java.util.stream.Stream; - - - -/** - * Eclipse Collections-based realizations of {@link IMultiLookup} - * - * @author Gabor Bergmann - * @since 2.0 - */ -class EclipseCollectionsMultiLookup { - - private EclipseCollectionsMultiLookup() {/* Hidden utility class constructor */} - - private static class MarkedSetImpl extends EclipseCollectionsSetMemory implements MarkedMemory.MarkedSet {} - private static class MarkedMultisetImpl extends EclipseCollectionsMultiset implements MarkedMemory.MarkedMultiset {} - private static class MarkedLongSetImpl extends EclipseCollectionsLongSetMemory implements MarkedMemory.MarkedSet {} - private static class MarkedLongMultisetImpl extends EclipseCollectionsLongMultiset implements MarkedMemory.MarkedMultiset {} - - public abstract static class FromObjects> - extends UnifiedMap implements IMultiLookupAbstract { - - @Override - public boolean equals(Object obj) { - return IMultiLookup.equals(this, obj); - } - @Override - public int hashCode() { - return IMultiLookup.hashCode(this); - } - - - @Override - public Object lowLevelPutIfAbsent(Key key, Value value) { - return super.putIfAbsent(key, value); - } - - @Override - public Object lowLevelGet(Key key) { - return super.get(key); - } - - @Override - public Object lowLevelGetUnsafe(Object key) { - return super.get(key); - } - - @Override - public Object lowLevelRemove(Key key) { - return super.remove(key); - } - - @Override - public void lowLevelPut(Key key, Object valueOrBucket) { - super.put(key, valueOrBucket); - } - @Override - public Iterable lowLevelValues() { - return super.values(); - } - @Override - public Set lowLevelKeySet() { - return super.keySet(); - } - @Override - public int lowLevelSize() { - return super.size(); - } - - @Override - public Stream distinctKeysStream() { - // may be more efficient than the default spliterator - return super.keySet().stream(); - } - - public abstract static class ToSets extends FromObjects> - implements IMultiLookupAbstract.ToSetsAbstract - { - public static class OfObjects extends ToSets { - @Override - public MarkedSet createMarkedSet() { - return new MarkedSetImpl(); - } - } - - public static class OfLongs extends ToSets { - @Override - public MarkedSet createMarkedSet() { - return new MarkedLongSetImpl(); - } - } - - } - - public abstract static class ToMultisets extends FromObjects> - implements IMultiLookupAbstract.ToMultisetsAbstract - { - public static class OfObjects extends ToMultisets { - @Override - public MarkedMultiset createMarkedMultiset() { - return new MarkedMultisetImpl(); - } - } - - public static class OfLongs extends ToMultisets { - @Override - public MarkedMultiset createMarkedMultiset() { - return new MarkedLongMultisetImpl(); - } - } - - } - - } - - public abstract static class FromLongs> - extends LongObjectHashMap implements IMultiLookupAbstract { - - @Override - public boolean equals(Object obj) { - return IMultiLookup.equals(this, obj); - } - @Override - public int hashCode() { - return IMultiLookup.hashCode(this); - } - - @Override - public Object lowLevelPutIfAbsent(Long key, Value value) { - Object old = super.get(key); - if (old == null) super.put(key, value); - return old; - } - - @Override - public Object lowLevelGet(Long key) { - return super.get(key); - } - - @Override - public Object lowLevelGetUnsafe(Object key) { - return key instanceof Long ? super.get((Long)key) : null; - } - - @Override - public Object lowLevelRemove(Long key) { - return super.remove(key); - } - - @Override - public void lowLevelPut(Long key, Object valueOrBucket) { - super.put(key, valueOrBucket); - } - @Override - public Iterable lowLevelValues() { - return super.values(); - } - @Override - public int lowLevelSize() { - return super.size(); - } - @Override - public Iterable lowLevelKeySet() { - return () -> EclipseCollectionsLongSetMemory.iteratorOf(FromLongs.super.keysView()); - } - - public abstract static class ToSets extends FromLongs> - implements IMultiLookupAbstract.ToSetsAbstract - { - public static class OfObjects extends ToSets { - @Override - public MarkedSet createMarkedSet() { - return new MarkedSetImpl(); - } - } - - public static class OfLongs extends ToSets { - @Override - public MarkedSet createMarkedSet() { - return new MarkedLongSetImpl(); - } - } - - } - - public abstract static class ToMultisets extends FromLongs> - implements IMultiLookupAbstract.ToMultisetsAbstract - { - public static class OfObjects extends ToMultisets { - @Override - public MarkedMultiset createMarkedMultiset() { - return new MarkedMultisetImpl(); - } - } - - public static class OfLongs extends ToMultisets { - @Override - public MarkedMultiset createMarkedMultiset() { - return new MarkedLongMultisetImpl(); - } - } - - } - - } - - -} - - diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java deleted file mode 100644 index 46977c8b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -/** - * @author Gabor Bergmann - * @since 1.7 - */ -public class EclipseCollectionsMultiset extends EclipseCollectionsBagMemory implements IMultiset { - - @Override - public boolean addOne(T value) { - int oldCount = super.getIfAbsent(value, 0); - - super.put(value, oldCount + 1); - - return oldCount == 0; - } - - @Override - public boolean addPositive(T value, int count) { - if (count < 0) { - throw new IllegalArgumentException("The count value must be positive!"); - } - - int oldCount = super.getIfAbsent(value, 0); - - super.put(value, oldCount + count); - - return oldCount == 0; - } - - @Override - public boolean addSigned(T value, int count) { - int oldCount = super.getIfAbsent(value, 0); - int newCount = oldCount + count; - - boolean becomesZero = newCount == 0; - if (newCount < 0) - throw new IllegalStateException(String.format( - "Cannot remove %d occurrences of value '%s' as only %d would remain in %s", - count, value, newCount, this)); - else if (becomesZero) - super.removeKey(value); - else // (newCount > 0) - super.put(value, newCount); - - return becomesZero || oldCount == 0; - } - - - @Override - public boolean removeOne(T value) { - return removeOneInternal(value, true); - } - - @Override - public boolean removeOneOrNop(T value) { - return removeOneInternal(value, false); - } - - /** - * @since 2.3 - */ - protected boolean removeOneInternal(T value, boolean throwIfImpossible) { - int oldCount = super.getIfAbsent(value, 0); - if (oldCount == 0) { - if (throwIfImpossible) throw new IllegalStateException(String.format( - "Cannot remove value '%s' that is not contained in %s", - value, this)); - else return false; - } - - int rest = oldCount - 1; - boolean empty = rest == 0; - - if (!empty) { - super.put(value, rest); - } else { - super.remove(value); - } - - return empty; - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java deleted file mode 100644 index 92f65246..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java +++ /dev/null @@ -1,94 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Set; - -import org.eclipse.collections.impl.set.mutable.UnifiedSet; - -/** - * @author Gabor Bergmann - * @since 2.0 - */ -public class EclipseCollectionsSetMemory extends UnifiedSet implements ISetMemory { - @Override - public int getCount(Value value) { - return super.contains(value) ? 1 : 0; - } - @Override - public int getCountUnsafe(Object value) { - return super.contains(value) ? 1 : 0; - } - @Override - public boolean containsNonZero(Value value) { - return super.contains(value); - } - - @Override - public boolean containsNonZeroUnsafe(Object value) { - return super.contains(value); - } - - @Override - public boolean addOne(Value value) { - return super.add(value); - } - - @Override - public boolean addSigned(Value value, int count) { - if (count == 1) return addOne(value); - else if (count == -1) return removeOne(value); - else throw new IllegalStateException(); - } - - @Override - public boolean removeOne(Value value) { - // Kept for binary compatibility - return ISetMemory.super.removeOne(value); - } - - @Override - public boolean removeOneOrNop(Value value) { - return super.remove(value); - } - - @Override - public void clearAllOf(Value value) { - super.remove(value); - } - - @Override - public Set distinctValues() { - return this; - } - - @Override - public Value theContainedVersionOf(Value value) { - return super.get(value); - } - - @Override - @SuppressWarnings("unchecked") - public Value theContainedVersionOfUnsafe(Object value) { - if (super.contains(value)) - return super.get((Value)value); - else return null; - } - - @Override - public int hashCode() { - return IMemoryView.hashCode(this); - } - @Override - public boolean equals(Object obj) { - return IMemoryView.equals(this, obj); - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java deleted file mode 100644 index a17b3a3f..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Collections; -import java.util.Iterator; -import java.util.Set; - -/** - * A singleton immutable empty memory. - * @author Gabor Bergmann - * @since 2.0 - * - */ -public class EmptyMemory implements IMemoryView { - - @SuppressWarnings("rawtypes") - private static final EmptyMemory INSTANCE = new EmptyMemory(); - - @SuppressWarnings("unchecked") - public static EmptyMemory instance() { - return INSTANCE; - } - - - - /** - * Singleton; hidden constructor - */ - private EmptyMemory() { - super(); - } - - @Override - public Iterator iterator() { - return Collections.emptySet().iterator(); - } - - @Override - public int getCount(T value) { - return 0; - } - - @Override - public int getCountUnsafe(Object value) { - return 0; - } - - @Override - public boolean containsNonZero(T value) { - return false; - } - - @Override - public boolean containsNonZeroUnsafe(Object value) { - return false; - } - - @Override - public int size() { - return 0; - } - - @Override - public boolean isEmpty() { - return true; - } - - @Override - public Set distinctValues() { - return Collections.emptySet(); - } - - @Override - public int hashCode() { - return IMemoryView.hashCode(this); - } - @Override - public boolean equals(Object obj) { - return IMemoryView.equals(this, obj); - } - - @Override - public String toString() { - return "{}"; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java deleted file mode 100644 index 8c2e54ad..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java +++ /dev/null @@ -1,32 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.function.Supplier; - -/** - * A cache is a simple key-value pair that stores calculated values for specific key objects - * - *

- * NOTE These caches are not expected to be used outside query backend implementations - * - * @author Zoltan Ujhelyi - * @since 1.7 - * @noreference This interface is not intended to be referenced by clients. - */ -public interface ICache { - - /** - * Return a selected value for the key object. If the value is not available in the cache yet, the given provider is - * called once - * @since 2.0 - */ - T getValue(Object key, Class clazz, Supplier valueProvider); - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java deleted file mode 100644 index 99a4cb3b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -/** - * An {@link IMemory} that represents the difference between two states of a set or {@link IMultiset}, and therefore - * may contain values with a negative multiplicity. - * - * @author Gabor Bergmann - * @since 1.7 - */ -public interface IDeltaBag extends IMemory { - - @Override - default boolean removeOneOrNop(T value) { - // makes no difference for delta bags - return removeOne(value); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java deleted file mode 100644 index ea788e53..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -/** - * A memory containing a positive or negative number of equal() copies for some values. - * During iterations, each distinct value is iterated only once. - * - *

Refined by:

    - *
  • {@link IMultiset}, which always contains values with a nonnegative multiplicity.
  • - *
  • {@link IDeltaBag}, which may contain values with negative multiplicity.
  • - *
  • {@link ISetMemory}, which is just a set (allowed multiplicities: 0 and 1).
  • - *
- * - * @author Gabor Bergmann - * @since 1.7 - * @noimplement This interface is not intended to be implemented by clients. - */ -public interface IMemory extends IMemoryView, Clearable { - - /** - * Adds one value occurrence to the memory. - * - * @return true if the tuple was not present before in the memory, or - * (in case of {@link IDeltaBag}) is no longer present in the memory - */ - boolean addOne(T value); - - /** - * Adds the given number of occurrences to the memory. The count value may or may not be negative. - *

Precondition if {@link IMultiset}: at least the given amount of occurrences exist, if count is negative. - *

Precondition if {@link ISetMemory}: count is +1 or -1, the latter is only allowed if the set contains the value. - * - * @param count - * the number of occurrences - * @return true if the tuple was not present before in the memory, or is no longer present in the memory - * @throws IllegalStateException if {@link IMultiset} or {@link ISetMemory} and the number of occurrences in the memory would underflow to negative - */ - boolean addSigned(T value, int count); - - /** - * Removes one occurrence of the given value from the memory. - *

Precondition if {@link IMultiset} or {@link ISetMemory}: the value must have a positive amount of occurrences in the memory. - * - * @return true if this was the the last occurrence of the value, or - * (in case of {@link IDeltaBag}) is the first negative occurrence of the value - * @throws IllegalStateException if {@link IMultiset} or {@link ISetMemory} and value had no occurrences in the memory - */ - boolean removeOne(T value); - - /** - * Removes one occurrence of the given value from the memory, if possible. - * - *

Memory is unchanged and false is returned if - * {@link IMultiset} or {@link ISetMemory} and value had no occurrences in the memory - * - * @return true if this was the the last occurrence of the value, or - * (in case of {@link IDeltaBag}) is the first negative occurrence of the value - * - * @since 2.3 - */ - boolean removeOneOrNop(T value); - - /** - * Removes all occurrences of the given value from the memory. - */ - void clearAllOf(T value); - - /** - * Empties out the memory. - */ - @Override - void clear(); - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java deleted file mode 100644 index add575c6..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java +++ /dev/null @@ -1,205 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.function.BiConsumer; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -/** - * A read-only view on a memory containing a positive or negative number of equal() copies for some values. - * During iterations, each distinct value is iterated only once. - * - *

See {@link IMemory}. - * - *

Implementors must provide semantic (not identity-based) hashCode() and equals() using the static helpers {@link #hashCode(IMemoryView)} and {@link #equals(IMemoryView, Object)} here. - * - * @author Gabor Bergmann - * - * @since 2.0 - */ -public interface IMemoryView extends Iterable { - - /** - * Returns the number of occurrences of the given value. - * - * @return the number of occurrences - */ - int getCount(T value); - - /** - * Returns the number of occurrences of the given value (which may be of any type). - * - * @return the number of occurrences - */ - int getCountUnsafe(Object value); - - /** - * @return true if the given value is contained with a nonzero multiplicity - */ - boolean containsNonZero(T value); - - /** - * @return true if the given value (which may be of any type) is contained with a nonzero multiplicity - */ - boolean containsNonZeroUnsafe(Object value); - - /** - * @return the number of distinct values - */ - int size(); - - /** - * - * @return iff contains at least one value with non-zero occurrences - */ - boolean isEmpty(); - - /** - * The set of distinct values - */ - Set distinctValues(); - - - /** - * Where supported, returns the stored element that is equal to the given value, or null if none. - * Useful for canonicalization in case of non-identity equals(). - * - *

For collections that do not support canonicalization, simply returns the argument if contained, null if none. - * - * @return a value equal to the argument if such a value is stored, or null if none - */ - default T theContainedVersionOf(T value) { - if (containsNonZero(value)) return value; else return null; - } - - /** - * Where supported, returns the stored element that is equal to the given value (of any type), - * or null if none. - * Useful for canonicalization in case of non-identity equals(). - * - *

For collections that do not support canonicalization, simply returns the argument if contained, null if none. - * - * @return a value equal to the argument if such a value is stored, or null if none - */ - @SuppressWarnings("unchecked") - default T theContainedVersionOfUnsafe(Object value) { - if (containsNonZeroUnsafe(value)) return (T) value; else return null; - } - - - /** - * @return an unmodifiable view of contained values with their multiplicities - */ - default Iterable> entriesWithMultiplicities() { - return () -> { - Iterator wrapped = distinctValues().iterator(); - return new Iterator> () { - @Override - public boolean hasNext() { - return wrapped.hasNext(); - } - - @Override - public Map.Entry next() { - T key = wrapped.next(); - int count = getCount(key); - return new Map.Entry(){ - @Override - public T getKey() { - return key; - } - - @Override - public Integer getValue() { - return count; - } - - @Override - public Integer setValue(Integer value) { - throw new UnsupportedOperationException(); - } - - @Override - public String toString() { - return String.format("%d of %s", count, key); - } - - }; - } - - }; - }; - } - - /** - * Process contained values with their multiplicities - */ - default void forEachEntryWithMultiplicities(BiConsumer entryConsumer) { - for (T value : distinctValues()) { - entryConsumer.accept(value, getCount(value)); - } - } - - - /** - * For compatibility with legacy code relying on element-to-integer maps. - * @return an unmodifiable view of contained values with their multiplicities - */ - public default Map asMap() { - return new MemoryViewBackedMapView<>(this); - } - - /** - * For compatibility with legacy code relying on element-to-integer maps. - * @return an unmodifiable view of contained values with their multiplicities - */ - public static IMemoryView fromMap(Map wrapped) { - return new MapBackedMemoryView<>(wrapped); - } - - /** - * @return a stream of values, iterable once - * @since 2.1 - */ - public default Stream asStream() { - return StreamSupport.stream(spliterator(), false); - } - - /** - * Provides semantic equality comparison. - */ - public static boolean equals(IMemoryView self, Object obj) { - if (obj instanceof IMemoryView) { - IMemoryView other = (IMemoryView) obj; - if (other.size() != self.size()) return false; - for (Entry entry : other.entriesWithMultiplicities()) { - if ( !entry.getValue().equals(self.getCountUnsafe(entry.getKey()))) - return false; - } - return true; - } - return false; - } - - /** - * Provides semantic hashCode() comparison. - */ - public static int hashCode(IMemoryView memory) { - int hashCode = 0; - for (T value : memory.distinctValues()) { - hashCode += value.hashCode() ^ Integer.hashCode(memory.getCount(value)); - } - return hashCode; - } -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java deleted file mode 100644 index 1ce1d2c9..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java +++ /dev/null @@ -1,216 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.stream.Stream; - -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; - -/** - * A multi-map that associates sets / multisets / delta sets of values to each key. - * - *

Implementors must provide semantic (not identity-based) hashCode() and equals() using the static helpers {@link #hashCode(IMultiLookup)} and {@link #equals(IMultiLookup, Object)} here. - * - * @author Gabor Bergmann - * @since 2.0 - * @noimplement This interface is not intended to be implemented by clients. - */ -public interface IMultiLookup { - - /** - * Returns true if this collection is empty, false otherwise. - * @since 2.2 - */ - boolean isEmpty(); - - - /** - * Returns true if there are any values associated with the given key. - * @param key a key for which associated values are sought - * @since 2.3 - */ - boolean lookupExists(Key key); - - /** - * Returns a (read-only) bucket of values associated with the given key. - * Clients must not modify the returned bucket. - * @param key a key for which associated values are sought - * @return null if key not found, a bucket of values otherwise - */ - IMemoryView lookup(Key key); - - /** - * Returns a (read-only) bucket of values associated with the given key. - * Clients must not modify the returned bucket. - * @param key a key for which associated values are sought - * @return a bucket of values, never null - */ - default IMemoryView lookupOrEmpty(Key key) { - IMemoryView bucket = lookup(key); - return bucket == null ? EmptyMemory.instance() : bucket; - } - - /** - * Returns a (read-only) bucket of values associated with the given key, while simultaneously removing them. - * Clients must not modify the returned bucket. - * @param key a key for which associated values are sought - * @return a bucket of values, never null - * @since 2.3 - */ - IMemoryView lookupAndRemoveAll(Key key); - - /** - * Returns a (read-only) bucket of values associated with the given key, which can be of any type. - * Clients must not modify the returned bucket. - * @param key a key for which associated values are sought (may or may not be of Key type) - * @return null if key not found, a bucket of values otherwise - */ - IMemoryView lookupUnsafe(Object key); - - /** - * Returns a (read-only) bucket of values associated with the given key. - * Clients must not modify the returned bucket. - * @param key a key for which associated values are sought (may or may not be of Key type) - * @return a bucket of values, never null - */ - default IMemoryView lookupUnsafeOrEmpty(Object key) { - IMemoryView bucket = lookupUnsafe(key); - return bucket == null ? EmptyMemory.instance() : bucket; - } - - - - /** - * @return the set of distinct keys that have values associated. - */ - Iterable distinctKeys(); - - /** - * @return the set of distinct keys that have values associated. - * @since 2.3 - */ - Stream distinctKeysStream(); - - /** - * @return the number of distinct keys that have values associated. - */ - int countKeys(); - - /** - * Iterates once over each distinct value. - */ - Iterable distinctValues(); - - /** - * Iterates once over each distinct value. - * @since 2.3 - */ - Stream distinctValuesStream(); - - - - /** - * How significant was the change? * - * @author Gabor Bergmann - */ - public enum ChangeGranularity { - /** - * First key-value pair with given key inserted, or last pair with given key deleted. - * (In case of delta maps, also if last negative key-value pair with given key neutralized.) - */ - KEY, - /** - * First occurrence of given key-value pair inserted, or last occurrence of the pair deleted, while key still has values associated. - * (In case of delta maps, also if last negative occurrence of key-value pair neutralized.) - */ - VALUE, - /** - * Duplicate key-value pair inserted or deleted. - */ - DUPLICATE - } - - /** - * Adds key-value pair to the lookup structure, or fails if not possible. - *

If the addition would cause duplicates but the bucket type does not allow it ({@link MemoryType#SETS}), - * the operation throws an {@link IllegalStateException}. - * @return the granularity of the change - * @throws IllegalStateException if addition would cause duplication that is not permitted - */ - public ChangeGranularity addPair(Key key, Value value); - /** - * Adds key-value pair to the lookup structure. - *

If the addition would cause duplicates but the bucket type does not allow it ({@link MemoryType#SETS}), - * the operation is silently ignored and {@link ChangeGranularity#DUPLICATE} is returned. - * @return the granularity of the change, or {@link ChangeGranularity#DUPLICATE} if addition would result in a duplicate and therefore ignored - * @since 2.3 - */ - public ChangeGranularity addPairOrNop(Key key, Value value); - /** - * Removes key-value pair from the lookup structure, or fails if not possible. - *

When attempting to remove a key-value pair with zero multiplicity from a non-delta bucket type - * ({@link MemoryType#SETS} or {@link MemoryType#MULTISETS}}), an {@link IllegalStateException} is thrown. - * @return the granularity of the change - * @throws IllegalStateException if removing non-existing element that is not permitted - */ - public ChangeGranularity removePair(Key key, Value value); - /** - * Removes key-value pair from the lookup structure. - *

When attempting to remove a key-value pair with zero multiplicity from a non-delta bucket type - * ({@link MemoryType#SETS} or {@link MemoryType#MULTISETS}}), - * the operation is silently ignored and {@link ChangeGranularity#DUPLICATE} is returned. - * @return the granularity of the change - * @throws IllegalStateException if removing non-existing element that is not permitted - * @since 2.3 - */ - public ChangeGranularity removePairOrNop(Key key, Value value); - - /** - * Updates multiplicity of key-value pair by a positive amount. - * - *

PRE: count > 0 - * - * @return the granularity of the change - * @throws IllegalStateException if addition would cause duplication that is not permitted - */ - public ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count); - - /** - * Empties out the lookup structure. - */ - public void clear(); - - /** - * Provides semantic equality comparison. - */ - public static boolean equals(IMultiLookup self, Object obj) { - if (obj instanceof IMultiLookup) { - IMultiLookup other = (IMultiLookup) obj; - if (other.countKeys() != self.countKeys()) return false; - for (Object key : other.distinctKeys()) { - if (! other.lookupUnsafe(key).equals(self.lookupUnsafe(key))) - return false; - } - return true; - } - return false; - } - - /** - * Provides semantic hashCode() comparison. - */ - public static int hashCode(IMultiLookup memory) { - int hashCode = 0; - for (Key key : memory.distinctKeys()) { - hashCode += key.hashCode() ^ memory.lookup(key).hashCode(); - } - return hashCode; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java deleted file mode 100644 index 8b1944c1..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java +++ /dev/null @@ -1,485 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Collections; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.stream.Stream; -import java.util.stream.StreamSupport; - -import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet; - -/** - * Specialized multimap implementation that saves memory - * by storing singleton value objects (multiplicity 1) instead of multiset buckets - * whenever there is only one value associated with a key. - * - *

See specialized {@link ToSetsAbstract}, {@link ToMultisetsAbstract} for various bucket types. - * - *

Implemented as a Key->Object map with invariant:

    - *
  • key maps to null if associated with no values; - *
  • key maps to a single Value iff it is associated with a single value of multiplicity +1; - *
  • key maps to Bucket otherwise - *
- * - * Note that due to the above invariant, handling +1 and -1 are asymmetric in case of delta maps. - * - *

Not intended as an API, but rather as a 'base class' for implementors. - * Realized as an interface with default implementations, instead of an abstract class, - * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. - * - *

Implementor should inherit from a Map-like class (primitive map possible) - * and bind the lowLevel* methods accordingly. - * - * @noreference This interface is not intended to be referenced by clients. - * @noimplement This interface is not intended to be implemented by clients. - * - * @author Gabor Bergmann - * @since 2.0 - * - * - */ -public interface IMultiLookupAbstract> extends IMultiLookup { - - // the following methods must be bound to a concrete Map-like structure (primitive implementation allowed) - - /** - * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map - */ - abstract Object lowLevelGet(Key key); - - /** - * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map - */ - abstract Object lowLevelGetUnsafe(Object key); - - /** - * Implementor shall bind to the low-level remove() or equivalent of the underlying Key-to-Object map - */ - abstract Object lowLevelRemove(Key key); - - /** - * Implementor shall bind to the low-level putIfAbsent() or equivalent of the underlying Key-to-Object map - */ - abstract Object lowLevelPutIfAbsent(Key key, Value value); - - /** - * Implementor shall bind to the low-level put() or equivalent of the underlying Key-to-Object map - */ - abstract void lowLevelPut(Key key, Object valueOrBucket); - - /** - * Implementor shall bind to the low-level values() or equivalent of the underlying Key-to-Object map - */ - abstract Iterable lowLevelValues(); - - /** - * Implementor shall bind to the low-level keySet() or equivalent of the underlying Key-to-Object map - */ - abstract Iterable lowLevelKeySet(); - - /** - * Implementor shall bind to the low-level size() or equivalent of the underlying Key-to-Object map - */ - abstract int lowLevelSize(); - - - // generic multi-lookup logic - - @Override - default boolean lookupExists(Key key) { - Object object = lowLevelGet(key); - return null != object; - } - - @Override - public default IMemoryView lookup(Key key) { - Object object = lowLevelGet(key); - if (object == null) return null; - if (object instanceof MarkedMemory) return (Bucket) object; - return yieldSingleton((Value)object); - } - - @Override - default IMemoryView lookupAndRemoveAll(Key key) { - Object object = lowLevelRemove(key); - if (object == null) return EmptyMemory.instance(); - if (object instanceof MarkedMemory) return (Bucket) object; - return yieldSingleton((Value)object); - } - - @Override - public default IMemoryView lookupUnsafe(Object key) { - Object object = lowLevelGetUnsafe(key); - if (object == null) return null; - if (object instanceof MarkedMemory) return (Bucket) object; - return yieldSingleton((Value)object); - } - - @Override - public default ChangeGranularity addPair(Key key, Value value) { - return addPairInternal(key, value, true); - } - - @Override - default ChangeGranularity addPairOrNop(Key key, Value value) { - return addPairInternal(key, value, false); - } - - public default ChangeGranularity addPairInternal(Key key, Value value, boolean throwIfImpossible) { - Object old = lowLevelPutIfAbsent(key, value); - boolean keyChange = (old == null); - - if (keyChange) { // key was not present - return ChangeGranularity.KEY; - } else { // key was already present - Bucket bucket; - if (old instanceof MarkedMemory) { // ... as collection - bucket = (Bucket) old; - } else { // ... as singleton - if (!this.duplicatesAllowed() && Objects.equals(value, old)) { - if (throwIfImpossible) - throw new IllegalStateException(); - else - return ChangeGranularity.DUPLICATE; - } - bucket = createSingletonBucket((Value) old); - lowLevelPut(key, bucket); - } - // will throw if forbidden duplicate, return false if allowed duplicate - if (addToBucket(bucket, value, throwIfImpossible)) { - // deltas may become empty or a singleton after addition! - if (negativesAllowed()) { - if (bucket.isEmpty()) { - lowLevelRemove(key); - return ChangeGranularity.KEY; - } else { - handleSingleton(key, bucket); - return ChangeGranularity.VALUE; - } - } else return ChangeGranularity.VALUE; - } else return ChangeGranularity.DUPLICATE; - } - } - - @Override - // TODO deltas not supproted yet - default ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count) { - if (count == 1) return addPair(key, value); - // count > 1, always end up with non-singleton bucket - - Object old = lowLevelGet(key); - boolean keyChange = (old == null); - - Bucket bucket; - if (keyChange) { // ... nothing associated to key yet - bucket = createSingletonBucket(value); - lowLevelPut(key, bucket); - --count; // one less to increment later - } else if (old instanceof MarkedMemory) { // ... as collection - bucket = (Bucket) old; - } else { // ... as singleton - bucket = createSingletonBucket((Value) old); - lowLevelPut(key, bucket); - } - - boolean newValue = bucket.addSigned(value, count); - - if (keyChange) return ChangeGranularity.KEY; - else if (newValue) return ChangeGranularity.VALUE; - else return ChangeGranularity.DUPLICATE; - } - - @Override - public default ChangeGranularity removePair(Key key, Value value) { - return removePairInternal(key, value, true); - } - - @Override - default ChangeGranularity removePairOrNop(Key key, Value value) { - return removePairInternal(key, value, false); - } - - public default ChangeGranularity removePairInternal(Key key, Value value, boolean throwIfImpossible) { - Object old = lowLevelGet(key); - if (old instanceof MarkedMemory) { // ... as collection - @SuppressWarnings("unchecked") - Bucket bucket = (Bucket) old; - // will throw if removing non-existent, return false if removing duplicate - boolean valueChange = removeFromBucket(bucket, value, throwIfImpossible); - handleSingleton(key, bucket); - if (valueChange) - return ChangeGranularity.VALUE; - else - return ChangeGranularity.DUPLICATE; - } else if (value.equals(old)) { // matching singleton - lowLevelRemove(key); - return ChangeGranularity.KEY; - } else { // different singleton, will produce a delta if possible - if (negativesAllowed()) { - Bucket deltaBucket = createDeltaBucket((Value) old, value); // will throw if no deltas supported - lowLevelPut(key, deltaBucket); - return ChangeGranularity.VALUE; // no key change - } else { - if (throwIfImpossible) - throw new IllegalStateException(); - else - return ChangeGranularity.DUPLICATE; - } - } - } - - public default void handleSingleton(Key key, Bucket bucket) { - Value remainingSingleton = asSingleton(bucket); - if (remainingSingleton != null) { // only one remains - lowLevelPut(key, remainingSingleton); - } - } - - @Override - public default Iterable distinctValues() { - return new Iterable() { - private final Iterator EMPTY_ITERATOR = Collections.emptySet().iterator(); - @Override - public Iterator iterator() { - return new Iterator() { - Iterator bucketIterator = lowLevelValues().iterator(); - Iterator elementIterator = EMPTY_ITERATOR; - - @Override - public boolean hasNext() { - return (elementIterator.hasNext() || bucketIterator.hasNext()); - } - - @Override - public Value next() { - if (elementIterator.hasNext()) - return elementIterator.next(); - else if (bucketIterator.hasNext()) { - Object bucket = bucketIterator.next(); - if (bucket instanceof MarkedMemory) { - elementIterator = - ((MarkedMemory) bucket).distinctValues().iterator(); - return elementIterator.next(); - } else { - elementIterator = EMPTY_ITERATOR; - return (Value) bucket; - } - } else - throw new NoSuchElementException(); - } - - /** - * Not implemented - */ - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - - }; - } - }; - } - - @Override - default Stream distinctValuesStream() { - return StreamSupport.stream(distinctValues().spliterator(), false); - } - - @Override - default Iterable distinctKeys() { - return lowLevelKeySet(); - } - - @Override - default Stream distinctKeysStream() { - return StreamSupport.stream(distinctKeys().spliterator(), false); - } - - @Override - default int countKeys() { - return lowLevelSize(); - } - - // the following methods are customized for bucket type - - /** - * @return iff negative multiplicites are allowed - */ - abstract boolean negativesAllowed(); - - /** - * @return iff larger-than-1 multiplicites are allowed - * @since 2.3 - */ - abstract boolean duplicatesAllowed(); - - /** - * Increases the multiplicity of the value in the bucket. - * @return true iff non-duplicate - * @throws IllegalStateException if disallowed duplication and throwIfImpossible is specified - */ - abstract boolean addToBucket(Bucket bucket, Value value, boolean throwIfImpossible); - - /** - * Decreases the multiplicity of the value in the bucket. - * @return false if removing duplicate value - * @throws IllegalStateException if removing non-existing value (unless delta map) and throwIfImpossible is specified - */ - abstract boolean removeFromBucket(Bucket bucket, Value value, boolean throwIfImpossible); - - /** - * Checks whether the bucket is a singleton, i.e. it contains a single value with multiplicity +1 - * @return the singleton value, or null if the bucket is not singleton - */ - abstract Value asSingleton(Bucket bucket); - - /** - * @return a new bucket consisting of a sole value - */ - abstract Bucket createSingletonBucket(Value value); - /** - * @return a read-only bucket consisting of a sole value, to be returned to the user - */ - default IMemoryView yieldSingleton(Value value) { - return new SingletonMemoryView<>(value); - } - - /** - * @param positive the previously existing value, or null if the delta is to contain a single negative tuple - * @return a new bucket consisting of a delta of two values - * @throws IllegalStateException if deltas not supported - */ - abstract Bucket createDeltaBucket(Value positive, Value negative); - - /** - * A multi-lookup whose buckets are sets. - * - *

Not intended as an API, but rather as a 'base class' for implementors. - * Realized as an interface with default implementations, instead of an abstract class, - * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. - * - *

Implementor should inherit from a Map-like class (primitive map possible) - * and bind the lowLevel* methods accordingly. - * - * @noreference This interface is not intended to be referenced by clients. - * @noimplement This interface is not intended to be implemented by clients. - * @author Gabor Bergmann - */ - public static interface ToSetsAbstract extends IMultiLookupAbstract> { - /** - * @return a fresh, empty marked set - */ - public MarkedSet createMarkedSet(); - - @Override - public default boolean negativesAllowed() { - return false; - } - @Override - default boolean duplicatesAllowed() { - return false; - } - - @Override - public default boolean addToBucket(MarkedSet bucket, Value value, boolean throwIfImpossible) { - if (bucket.addOne(value)) return true; - else if (throwIfImpossible) throw new IllegalStateException(); - else return false; - } - - @Override - public default boolean removeFromBucket(MarkedSet bucket, Value value, boolean throwIfImpossible) { - return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value); - } - - @Override - public default Value asSingleton(MarkedSet bucket) { - return bucket.size() == 1 ? bucket.iterator().next() : null; - } - - @Override - public default MarkedSet createSingletonBucket(Value value) { - MarkedSet result = createMarkedSet(); - result.addOne(value); - return result; - } - - @Override - public default MarkedSet createDeltaBucket(Value positive, Value negative) { - throw new IllegalStateException(); - } - } - - /** - * A multi-lookup whose buckets are multisets. - * - *

Not intended as an API, but rather as a 'base class' for implementors. - * Realized as an interface with default implementations, instead of an abstract class, - * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. - * - *

Implementor should inherit from a Map-like class (primitive map possible) - * and bind the lowLevel* methods accordingly. - * - * @noreference This interface is not intended to be referenced by clients. - * @noimplement This interface is not intended to be implemented by clients. - * @author Gabor Bergmann - */ - public static interface ToMultisetsAbstract extends IMultiLookupAbstract> { - /** - * @return a fresh, empty marked multiset - */ - public MarkedMemory.MarkedMultiset createMarkedMultiset(); - - @Override - public default boolean negativesAllowed() { - return false; - } - @Override - default boolean duplicatesAllowed() { - return true; - } - - @Override - public default boolean addToBucket(MarkedMemory.MarkedMultiset bucket, Value value, boolean throwIfImpossible) { - return bucket.addOne(value); - } - - @Override - public default boolean removeFromBucket(MarkedMemory.MarkedMultiset bucket, Value value, boolean throwIfImpossible) { - return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value); - } - - @Override - public default Value asSingleton(MarkedMemory.MarkedMultiset bucket) { - if (bucket.size() != 1) return null; - Value candidate = bucket.iterator().next(); - return bucket.getCount(candidate) == 1 ? candidate : null; - } - - @Override - public default MarkedMemory.MarkedMultiset createSingletonBucket(Value value) { - MarkedMemory.MarkedMultiset result = createMarkedMultiset(); - result.addOne(value); - return result; - } - - @Override - public default MarkedMemory.MarkedMultiset createDeltaBucket(Value positive, Value negative) { - throw new IllegalStateException(); - } - } - - - // TODO add ToDeltaBagsAbstract - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java deleted file mode 100644 index bdd5d597..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -/** - * An {@link IMemory} that always contains values with a nonnegative multiplicity. - * - *

In case a write operation caused underflow, an {@link IllegalStateException} is thrown. - * - * @author Gabor Bergmann - * @since 1.7 - */ -public interface IMultiset extends IMemory { - - /** - * Adds the given number of occurrences to the memory. The count value must be a positive number. - * - * @param count - * the number of occurrences - * @return true if the tuple was not present before in the memory - */ - boolean addPositive(T value, int count); - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java deleted file mode 100644 index cd25dc95..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java +++ /dev/null @@ -1,30 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.function.Function; -import java.util.function.Supplier; - -import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; - -/** - * A provider interface useful in various registry instances. - * - * @author Zoltan Ujhelyi - * - */ -public interface IProvider extends Supplier{ - - public final class ProvidedValueFunction implements Function, PQuery> { - @Override - public PQuery apply(IProvider input) { - return (input == null) ? null : input.get(); - } - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java deleted file mode 100644 index 0c03da48..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.function.BiConsumer; - -/** - * An {@link IMemory} that always contains values with a 0 or +1 multiplicity. - * - *

In case a write operation causes underflow or overflow, an {@link IllegalStateException} is thrown. - * - * @author Gabor Bergmann - * @since 2.0 - */ -public interface ISetMemory extends IMemory { - - @Override - default void forEachEntryWithMultiplicities(BiConsumer entryConsumer) { - for (T t : this.distinctValues()) entryConsumer.accept(t, 1); - } - - - @Override - default boolean removeOne(T value) { - if (!removeOneOrNop(value)) - throw new IllegalStateException(); - return true; - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java deleted file mode 100644 index 3be078bd..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java +++ /dev/null @@ -1,102 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Iterator; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.function.BiConsumer; - -/** - * Wraps a Map (mapping elements to non-zero multiplicities) into an {@link IMemoryView}. - * - * @author Gabor Bergmann - * @since 2.0 - */ -public class MapBackedMemoryView implements IMemoryView { - - private Map wrapped; - - /** - * @param wrapped an equivalent map from contained objects to multiplicities - */ - protected MapBackedMemoryView(Map wrapped) { - super(); - this.wrapped = wrapped; - } - - @Override - public Iterator iterator() { - return wrapped.keySet().iterator(); - } - - @Override - public int getCount(T value) { - return getCountUnsafe(value); - } - - @Override - public int getCountUnsafe(Object value) { - Integer count = wrapped.get(value); - return count == null ? 0 : count; - } - - @Override - public boolean containsNonZero(T value) { - return wrapped.containsKey(value); - } - - @Override - public boolean containsNonZeroUnsafe(Object value) { - return wrapped.containsKey(value); - } - - @Override - public int size() { - return wrapped.size(); - } - - @Override - public boolean isEmpty() { - return wrapped.isEmpty(); - } - - @Override - public Set distinctValues() { - return wrapped.keySet(); - } - - - @Override - public void forEachEntryWithMultiplicities(BiConsumer entryConsumer) { - for (Entry entry : wrapped.entrySet()) { - entryConsumer.accept(entry.getKey(), entry.getValue()); - } - } - - @Override - public Iterable> entriesWithMultiplicities() { - return wrapped.entrySet(); - } - - @Override - public int hashCode() { - return IMemoryView.hashCode(this); - } - @Override - public boolean equals(Object obj) { - return IMemoryView.equals(this, obj); - } - - @Override - public String toString() { - return wrapped.toString(); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java deleted file mode 100644 index d22dcbe7..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java +++ /dev/null @@ -1,21 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -/** - * Internal marker type, must only be instantiated inside implementors of IMultiLookupImpl - * @noimplement This interface is not intended to be implemented by clients. - * @since 2.0 - */ -public interface MarkedMemory extends IMemory { - - static interface MarkedSet extends MarkedMemory {} - static interface MarkedMultiset extends MarkedMemory {} - static interface MarkedDeltaBag extends MarkedMemory {} -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java deleted file mode 100644 index 49711a89..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java +++ /dev/null @@ -1,117 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -/** - * A partial and read-only Map implementation, mapping elements to multiplicities backed by an {@link IMemoryView}. - * - *

Not implemented: write methods. - * - *

Inefficiently implemented: {@link #containsValue(Object)}, {@link #values()}, {@link #entrySet()}. - * - * @author Gabor Bergmann - * @since 2.0 - */ -public class MemoryViewBackedMapView implements Map { - - private static final String READ_ONLY = "Read only"; - private final IMemoryView wrapped; - - /** - * @param wrapped a memory view whose contents are to be exposed as an element-to-integer map. - */ - protected MemoryViewBackedMapView(IMemoryView wrapped) { - super(); - this.wrapped = wrapped; - } - - @Override - public int size() { - return wrapped.size(); - } - - @Override - public boolean isEmpty() { - return wrapped.isEmpty(); - } - - @Override - public boolean containsKey(Object key) { - return wrapped.containsNonZeroUnsafe(key); - } - - @Override - public boolean containsValue(Object value) { - if (value instanceof Integer) { - for (Entry entry : wrapped.entriesWithMultiplicities()) { - if (entry.getValue().equals(value)) return true; - } - } - return false; - } - - @Override - public Integer put(T key, Integer value) { - throw new UnsupportedOperationException(READ_ONLY); - } - - @Override - public Integer get(Object key) { - int count = wrapped.getCountUnsafe(key); - if (count == 0) return null; else return count; - } - - @Override - public Integer remove(Object key) { - throw new UnsupportedOperationException(READ_ONLY); - } - - @Override - public void putAll(Map m) { - throw new UnsupportedOperationException(READ_ONLY); - } - - @Override - public void clear() { - throw new UnsupportedOperationException(READ_ONLY); - } - - @Override - public Set keySet() { - return wrapped.distinctValues(); - } - - @Override - public Collection values() { - Collection result = new ArrayList<>(); - wrapped.forEachEntryWithMultiplicities((value, count) -> result.add(count)); - return result; - } - - @Override - public Set> entrySet() { - Set> result = new HashSet<>(); - for (Entry entry : wrapped.entriesWithMultiplicities()) { - result.add(entry); - } - return result; - } - - - @Override - public String toString() { - return wrapped.toString(); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java deleted file mode 100644 index e9e5e3a0..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java +++ /dev/null @@ -1,208 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.function.Supplier; - -/** - * This class was motivated by the similar Preconditions class from Guava to provide simple precondition checking - * functionality. However, as starting with version 2.0 the runtime of VIATRA Query should not depend on Guava, the - * relevant functionality of the Preconditions checking functionality will be implemented here. - * - * @author Zoltan Ujhelyi - * @since 2.0 - * - */ -public final class Preconditions { - - private Preconditions() { - /* Utility class constructor */ } - - /** - * Ensures the truth of an expression involving one or more parameters to the calling method. - * - * @param expression - * a boolean expression - * @throws IllegalArgumentException - * if {@code expression} is false - */ - public static void checkArgument(boolean expression) { - if (!expression) { - throw new IllegalArgumentException(); - } - } - - /** - * Ensures the truth of an expression involving one or more parameters to the calling method. - * - * @param expression - * a boolean expression - * @param errorMessage - * the exception message to use if the check fails - * @throws IllegalArgumentException - * if {@code expression} is false - */ - public static void checkArgument(boolean expression, String errorMessage) { - if (!expression) { - throw new IllegalArgumentException(errorMessage); - } - } - - /** - * Ensures the truth of an expression involving one or more parameters to the calling method. - * - * @param expression - * a boolean expression - * @param errorMessageTemplate - * a template for the exception message should the check fail using the Java Formatter syntax; the same - * as used by {@link String#format(String, Object...)}. - * @param errorMessageArgs - * the arguments to be substituted into the message template. - * @throws IllegalArgumentException - * if {@code expression} is false - * @throws NullPointerException - * if the check fails and either {@code errorMessageTemplate} or {@code errorMessageArgs} is null (don't - * let this happen) - */ - public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { - if (!expression) { - throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs)); - } - } - - /** - * Ensures the truth of an expression involving one or more parameters to the calling method. - * - * @param expression - * a boolean expression - * @param messageSupplier a supplier that is called to calculate the error message if necessary - * @throws IllegalArgumentException - * if {@code expression} is false - */ - public static void checkArgument(boolean expression, Supplier messageSupplier) { - if (!expression) { - throw new IllegalArgumentException(messageSupplier.get()); - } - } - - /** - * Ensures the truth of an expression involving one or more fields of a class. - * - * @param expression - * a boolean expression - * @throws IllegalStateException - * if {@code expression} is false - */ - public static void checkState(boolean expression) { - if (!expression) { - throw new IllegalStateException(); - } - } - - /** - * Ensures the truth of an expression involving one or more fields of a class. - * - * @param expression - * a boolean expression - * @param errorMessage - * the exception message to use if the check fails - * @throws IllegalStateException - * if {@code expression} is false - */ - public static void checkState(boolean expression, String errorMessage) { - if (!expression) { - throw new IllegalStateException(errorMessage); - } - } - - /** - * Ensures the truth of an expression involving one or more fields of a class. - * - * @param expression - * a boolean expression - * @param errorMessageTemplate - * a template for the exception message should the check fail using the Java Formatter syntax; the same - * as used by {@link String#format(String, Object...)}. - * @param errorMessageArgs - * the arguments to be substituted into the message template. - * @throws IllegalStateException - * if {@code expression} is false - * @throws NullPointerException - * if the check fails and either {@code errorMessageTemplate} or {@code errorMessageArgs} is null (don't - * let this happen) - */ - public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { - if (!expression) { - throw new IllegalStateException(String.format(errorMessageTemplate, errorMessageArgs)); - } - } - - /** - * Ensures the truth of an expression involving one or more fields of a class. - * - * @param expression - * a boolean expression - * @param messageSupplier a supplier that is called to calculate the error message if necessary - * @throws IllegalStateException - * if {@code expression} is false - */ - public static void checkState(boolean expression, Supplier messageSupplier) { - if (!expression) { - throw new IllegalStateException(messageSupplier.get()); - } - } - - /** - * Ensures that an index is appropriate for a list or array of given size. - * - * @param index - * @param size - * @throws IndexOutOfBoundsException - * if index is negative or is greater or equal to size - */ - public static void checkElementIndex(int index, int size) { - if (index < 0 || index >= size) { - throw new IndexOutOfBoundsException(); - } - } - - /** - * Ensures that an index is appropriate for a list or array of given size. - * - * @param index - * @param size - * @param errorMessageTemplate - * a template for the exception message should the check fail using the Java Formatter syntax; the same - * as used by {@link String#format(String, Object...)}. - * @param errorMessageArgs - * the arguments to be substituted into the message template. - * @throws IndexOutOfBoundsException - * if index is negative or is greater or equal to size - */ - public static void checkElementIndex(int index, int size, String errorMessageTemplate, Object... errorMessageArgs) { - if (index < 0 || index >= size) { - throw new IndexOutOfBoundsException(String.format(errorMessageTemplate, errorMessageArgs)); - } - } - - /** - * Ensures that an index is appropriate for a list or array of given size. - * - * @param index - * @param size - * @param messageSupplier a supplier that is called to calculate the error message if necessary - * @throws IndexOutOfBoundsException - * if index is negative or is greater or equal to size - */ - public static void checkElementIndex(int index, int size, Supplier messageSupplier) { - if (index < 0 || index >= size) { - throw new IndexOutOfBoundsException(messageSupplier.get()); - } - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java deleted file mode 100644 index c4e6b5af..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java +++ /dev/null @@ -1,44 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Supplier; - -/** - * @author Zoltan Ujhelyi - * @since 1.7 - * @noreference This class is not intended to be referenced by clients. - */ -public class PurgableCache implements ICache { - - Map storage = new HashMap<>(); - - @Override - @SuppressWarnings("unchecked") - public T getValue(Object key, Class clazz, Supplier valueProvider) { - if (storage.containsKey(key)) { - Object value = storage.get(key); - Preconditions.checkState(clazz.isInstance(value), "Cache stores for key %s a value of %s that is incompatible with the requested type %s", key, value, clazz); - return (T) value; - } else { - T value = valueProvider.get(); - storage.put(key, value); - return value; - } - } - - /** - * Removes all values stored in the cache - */ - public void purge() { - storage.clear(); - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java deleted file mode 100644 index 3749fe06..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java +++ /dev/null @@ -1,90 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - * - * Contributors: - * Gabor Bergmann - initial API and implementation - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -/** - * This class was motivated by the similar Sets class from Guava to provide simple set manipulation - * functionality. However, as starting with version 2.3 the runtime of VIATRA Query should not depend on Guava, - * not even internally, the relevant subset of Sets methods will be reimplemented here. - * - *

The current approach is to delegate to Eclipse Collections wherever possible. - * Such glue methods are useful so that downstream clients can avoid directly depending on Eclipse Collections. - * - *

Without an equivalent from Eclipse Collections, {@link #cartesianProduct(List)} is implemented here from scratch. - * - * @author Gabor Bergmann - * @since 2.3 - */ -public final class Sets { - - /** - * @since 2.4 - */ - public static Set newSet(Iterable elements) { - return org.eclipse.collections.impl.factory.Sets.mutable.ofAll(elements); - } - - public static Set intersection(Set left, Set right) { - return org.eclipse.collections.impl.factory.Sets.intersect(left, right); - } - - public static Set difference(Set left, Set right) { - return org.eclipse.collections.impl.factory.Sets.difference(left, right); - } - - public static Set union(Set left, Set right) { - return org.eclipse.collections.impl.factory.Sets.union(left, right); - } - - public static Set> powerSet(Set set) { - return org.eclipse.collections.impl.factory.Sets.powerSet(set); - } - - public static Set> cartesianProduct(List> setsList) { - - class Suffix { // simple immutable linked list - private A head; - private Suffix next; - - public Suffix(A head, Suffix next) { - super(); - this.head = head; - this.next = next; - } - - public List toList() { - ArrayList result = new ArrayList<>(); - for (Suffix cursor = this; cursor!=null; cursor = cursor.next) - result.add(cursor.head); - return result; - } - } - - // build result lists from end to start, in the form of suffixes - Stream suffixes = Stream.of((Suffix) null /* empty suffix*/); - for (int i = setsList.size()-1; i>=0; --i) { // iterate sets in reverse order - Set set = setsList.get(i); - suffixes = suffixes.flatMap(suffix -> set.stream().map(newElement -> new Suffix(newElement, suffix))); - } - - - return suffixes.map(Suffix::toList).collect(Collectors.toSet()); - } - - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java deleted file mode 100644 index 8f8bc228..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Objects; - -/** - * A piece of data associated with a direction. - * - * @author Tamas Szabo - * @since 2.4 - */ -public class Signed> { - - private final Payload payload; - private final Direction direction; - - public Signed(final Direction direction, final Payload payload) { - this.payload = payload; - this.direction = direction; - } - - public Payload getPayload() { - return payload; - } - - public Direction getDirection() { - return direction; - } - - @Override - public int hashCode() { - return Objects.hash(direction, payload); - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } else if (obj == null || this.getClass() != obj.getClass()) { - return false; - } else { - @SuppressWarnings("rawtypes") - final Signed other = (Signed) obj; - return direction == other.direction && Objects.equals(payload, other.payload); - } - } - - @Override - public String toString() { - return this.direction.asSign() + this.payload.toString(); - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java deleted file mode 100644 index cc5963f7..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2015, Zoltan Ujhelyi, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -/** - * A provider implementation that always returns the same object instance. - * @author Zoltan Ujhelyi - */ -public class SingletonInstanceProvider implements IProvider{ - - private T instance; - - public SingletonInstanceProvider(T instance) { - Preconditions.checkArgument(instance != null, "Instance parameter must not be null."); - this.instance = instance; - } - - @Override - public T get() { - return instance; - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java deleted file mode 100644 index b303f9ad..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java +++ /dev/null @@ -1,105 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Collections; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Set; - -/** - * An immutable memory view that consists of a single non-null element with multiplicity 1. - * @author Gabor Bergmann - * @since 2.0 - */ -public final class SingletonMemoryView implements IMemoryView { - - private Value wrapped; - private static final int ONE_HASH = Integer.valueOf(1).hashCode(); - - public SingletonMemoryView(Value value) { - this.wrapped = value; - } - - @Override - public Iterator iterator() { - return new Iterator() { - boolean hasNext = true; - - @Override - public boolean hasNext() { - return hasNext; - } - - @Override - public Value next() { - if (hasNext) { - hasNext = false; - return wrapped; - } else throw new NoSuchElementException(); - } - }; - } - - @Override - public int getCount(Value value) { - return wrapped.equals(value) ? 1 : 0; - } - - @Override - public int getCountUnsafe(Object value) { - return wrapped.equals(value) ? 1 : 0; - } - - @Override - public boolean containsNonZero(Value value) { - return wrapped.equals(value); - } - - @Override - public boolean containsNonZeroUnsafe(Object value) { - return wrapped.equals(value); - } - - @Override - public int size() { - return 1; - } - - @Override - public boolean isEmpty() { - return false; - } - - @Override - public Set distinctValues() { - return Collections.singleton(wrapped); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof IMemoryView) { - IMemoryView other = (IMemoryView) obj; - if (1 != other.size()) return false; - if (1 != other.getCountUnsafe(wrapped)) return false; - return true; - } - return false; - } - - @Override - public int hashCode() { - return wrapped.hashCode() ^ ONE_HASH; - } - - @Override - public String toString() { - return "{" + wrapped + "}"; - } -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java deleted file mode 100644 index 90fcad4d..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java +++ /dev/null @@ -1,517 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util; - -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.NavigableMap; -import java.util.Set; -import java.util.TreeMap; - -import tools.refinery.viatra.runtime.matchers.tuple.ITuple; -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.util.resumable.Resumable; -import tools.refinery.viatra.runtime.matchers.util.resumable.UnmaskedResumable; -import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; -import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; - -/** - * A timely memory implementation that incrementally maintains the {@link Timeline}s of tuples. The memory is capable of - * lazy folding (see {@link Resumable}). - * - * @author Tamas Szabo - * @since 2.3 - */ -public class TimelyMemory> implements Clearable, UnmaskedResumable { - - protected final Map> counters; - protected final Map> timelines; - public final TreeMap> foldingState; - protected final Set presentAtInfinity; - protected final boolean isLazy; - protected final Diff EMPTY_DIFF = new Diff(); - - public TimelyMemory() { - this(false); - } - - public TimelyMemory(final boolean isLazy) { - this.counters = CollectionsFactory.createMap(); - this.timelines = CollectionsFactory.createMap(); - this.presentAtInfinity = CollectionsFactory.createSet(); - this.isLazy = isLazy; - if (isLazy) { - this.foldingState = CollectionsFactory.createTreeMap(); - } else { - this.foldingState = null; - } - } - - @Override - public Set getResumableTuples() { - if (this.foldingState == null || this.foldingState.isEmpty()) { - return Collections.emptySet(); - } else { - return this.foldingState.firstEntry().getValue().keySet(); - } - } - - @Override - public Timestamp getResumableTimestamp() { - if (this.foldingState == null || this.foldingState.isEmpty()) { - return null; - } else { - return this.foldingState.firstKey(); - } - } - - /** - * Registers the given folding state for the specified timestamp and tuple. If there is already a state stored, the - * two states will be merged together. - */ - protected void addFoldingState(final Tuple tuple, final FoldingState state, final Timestamp timestamp) { - assert state.diff != 0; - final Map tupleMap = this.foldingState.computeIfAbsent(timestamp, - k -> CollectionsFactory.createMap()); - tupleMap.compute(tuple, (k, v) -> { - return v == null ? state : v.merge(state); - }); - } - - @Override - public Map> resumeAt(final Timestamp timestamp) { - Timestamp current = this.getResumableTimestamp(); - if (current == null) { - throw new IllegalStateException("There is othing to fold!"); - } else if (current.compareTo(timestamp) != 0) { - // It can happen that already registered folding states end up having zero diffs, - // and we are instructed to continue folding at a timestamp that is higher - // than the lowest timestamp with a folding state. - // However, we only do garbage collection in doFoldingState, so now it is time to - // first clean up those states with zero diffs. - while (current != null && current.compareTo(timestamp) < 0) { - final Map tupleMap = this.foldingState.remove(current); - for (final Entry entry : tupleMap.entrySet()) { - final Tuple key = entry.getKey(); - final FoldingState value = entry.getValue(); - if (value.diff != 0) { - throw new IllegalStateException("Expected zero diff during garbage collection at " + current - + ", but the diff was " + value.diff + "!"); - } - doFoldingStep(key, value, current); - } - current = this.getResumableTimestamp(); - } - if (current == null || current.compareTo(timestamp) != 0) { - throw new IllegalStateException("Expected to continue folding at " + timestamp + "!"); - } - } - - final Map> diffMap = CollectionsFactory.createMap(); - final Map tupleMap = this.foldingState.remove(timestamp); - for (final Entry entry : tupleMap.entrySet()) { - final Tuple key = entry.getKey(); - final FoldingState value = entry.getValue(); - diffMap.put(key, doFoldingStep(key, value, timestamp)); - } - - if (this.foldingState.get(timestamp) != null) { - throw new IllegalStateException( - "Folding at " + timestamp + " produced more folding work at the same timestamp!"); - } - - return diffMap; - } - - protected Diff doFoldingStep(final Tuple tuple, final FoldingState state, final Timestamp timestamp) { - final CumulativeCounter counter = getCounter(tuple, timestamp); - if (state.diff == 0) { - gcCounters(counter, tuple, timestamp); - return EMPTY_DIFF; - } else { - final Diff resultDiff = new Diff<>(); - final Timestamp nextTimestamp = this.counters.get(tuple).higherKey(timestamp); - - final int oldCumulative = counter.cumulative; - - counter.cumulative += state.diff; - - computeDiffsLazy(state.diff < 0 ? Direction.DELETE : Direction.INSERT, oldCumulative, counter.cumulative, - timestamp, nextTimestamp, resultDiff); - - gcCounters(counter, tuple, timestamp); - updateTimeline(tuple, resultDiff); - - // prepare folding state for next timestamp - if (nextTimestamp != null) { - // propagate the incoming diff, not the diff stored in counter - addFoldingState(tuple, new FoldingState(state.diff), nextTimestamp); - } - - return resultDiff; - } - } - - /** - * On-demand initializes and returns the counter for the given tuple and timestamp. - */ - protected CumulativeCounter getCounter(final Tuple tuple, final Timestamp timestamp) { - final TreeMap counterTimeline = this.counters.computeIfAbsent(tuple, - k -> CollectionsFactory.createTreeMap()); - - final CumulativeCounter counter = counterTimeline.computeIfAbsent(timestamp, k -> { - final Entry previousCounter = counterTimeline.lowerEntry(k); - final int previousCumulative = previousCounter == null ? 0 : previousCounter.getValue().cumulative; - return new CumulativeCounter(0, previousCumulative); - }); - - return counter; - } - - /** - * Garbage collects the counter of the given tuple and timestamp if the new diff is zero. - */ - protected void gcCounters(final CumulativeCounter counter, final Tuple tuple, final Timestamp timestamp) { - if (counter.diff == 0) { - final TreeMap counterMap = this.counters.get(tuple); - counterMap.remove(timestamp); - if (counterMap.isEmpty()) { - this.counters.remove(tuple); - } - } - } - - /** - * Utility method that computes the timeline diffs in case of lazy memories. The diffs will be inserted into the - * input parameter. This method computes diffs for entire plateaus that spans from timestamp to nextTimestamp. - * - * Compared to the eager version of this method, the lazy version makes use of both the old and the new cumulative - * values because it can happen that the cumulative is incremented by a value that is larger than 1 (as folding - * states are merged together). This means that we cant decide whether the cumulative became positive by comparing - * the new value to 1. - */ - protected void computeDiffsLazy(final Direction direction, final int oldCumulative, final int newCumulative, - final Timestamp timestamp, final Timestamp nextTimestamp, final Diff diffs) { - if (direction == Direction.INSERT) { - if (newCumulative == 0) { - throw new IllegalStateException("Cumulative count can never be negative!"); - } else { - if (oldCumulative == 0 /* current became positive */) { - // (1) either we sent out a DELETE before and now we need to cancel it, - // (2) or we just INSERT this for the first time - diffs.add(new Signed<>(Direction.INSERT, timestamp)); - if (nextTimestamp != null) { - diffs.add(new Signed<>(Direction.DELETE, nextTimestamp)); - } - } else /* current stays positive */ { - // nothing to do - } - } - } else { - if (newCumulative < 0) { - throw new IllegalStateException("Cumulative count can never be negative!"); - } else { - if (newCumulative == 0 /* current became zero */) { - diffs.add(new Signed<>(Direction.DELETE, timestamp)); - if (nextTimestamp != null) { - diffs.add(new Signed<>(Direction.INSERT, nextTimestamp)); - } - } else /* current stays positive */ { - // nothing to do - } - } - } - } - - /** - * Utility method that computes the timeline diffs in case of eager memories. The diffs will be inserted into the - * input parameter. This method computes diffs that describe momentary changes instead of plateaus. Returns a - * {@link SignChange} that describes how the sign has changed at the given timestamp. - */ - protected SignChange computeDiffsEager(final Direction direction, final CumulativeCounter counter, - final SignChange signChangeAtPrevious, final Timestamp timestamp, final Diff diffs) { - if (direction == Direction.INSERT) { - if (counter.cumulative == 0) { - throw new IllegalStateException("Cumulative count can never be negative!"); - } else { - if (counter.cumulative == 1 /* current became positive */) { - if (signChangeAtPrevious != SignChange.BECAME_POSITIVE) { - // (1) either we sent out a DELETE before and now we need to cancel it, - // (2) or we just INSERT this for the first time - diffs.add(new Signed<>(Direction.INSERT, timestamp)); - } else { - // we have already emitted this at the previous timestamp - // both previous and current became positive - throw new IllegalStateException( - "This would mean that the diff at current is 0 " + counter.diff); - } - - // remember for next timestamp - return SignChange.BECAME_POSITIVE; - } else /* current stays positive */ { - if (signChangeAtPrevious == SignChange.BECAME_POSITIVE) { - // we sent out an INSERT before and now the timeline is positive already starting at previous - // we need to cancel the effect of this with a DELETE - diffs.add(new Signed<>(Direction.DELETE, timestamp)); - } else { - // this is normal, both previous and current was positive and stays positive - } - - // remember for next timestamp - return SignChange.IRRELEVANT; - } - } - } else { - if (counter.cumulative < 0) { - throw new IllegalStateException("Cumulative count can never be negative!"); - } else { - if (counter.cumulative == 0 /* current became zero */) { - if (signChangeAtPrevious != SignChange.BECAME_ZERO) { - // (1) either we sent out a INSERT before and now we need to cancel it, - // (2) or we just DELETE this for the first time - diffs.add(new Signed<>(Direction.DELETE, timestamp)); - } else { - // we have already emitted this at the previous timestamp - // both previous and current became zero - throw new IllegalStateException( - "This would mean that the diff at current is 0 " + counter.diff); - } - - // remember for next timestamp - return SignChange.BECAME_ZERO; - } else /* current stays positive */ { - if (signChangeAtPrevious == SignChange.BECAME_ZERO) { - // we sent out a DELETE before and now the timeline is zero already starting at previous - // we need to cancel the effect of this with a INSERT - diffs.add(new Signed<>(Direction.INSERT, timestamp)); - } else { - // this is normal, both previous and current was positive and stays positive - } - - // remember for next timestamp - return SignChange.IRRELEVANT; - } - } - } - } - - public Diff put(final Tuple tuple, final Timestamp timestamp) { - if (this.isLazy) { - return putLazy(tuple, timestamp); - } else { - return putEager(tuple, timestamp); - } - } - - public Diff remove(final Tuple tuple, final Timestamp timestamp) { - if (this.isLazy) { - return removeLazy(tuple, timestamp); - } else { - return removeEager(tuple, timestamp); - } - } - - protected Diff putEager(final Tuple tuple, final Timestamp timestamp) { - final Diff resultDiff = new Diff<>(); - final CumulativeCounter counter = getCounter(tuple, timestamp); - ++counter.diff; - - // before the INSERT timestamp, no change at all - // it cannot happen that those became positive in this round - SignChange signChangeAtPrevious = SignChange.IRRELEVANT; - - final NavigableMap nextCounters = this.counters.get(tuple).tailMap(timestamp, - true); - for (final Entry currentEntry : nextCounters.entrySet()) { - final Timestamp currentTimestamp = currentEntry.getKey(); - final CumulativeCounter currentCounter = currentEntry.getValue(); - ++currentCounter.cumulative; - signChangeAtPrevious = computeDiffsEager(Direction.INSERT, currentCounter, signChangeAtPrevious, - currentTimestamp, resultDiff); - } - - gcCounters(counter, tuple, timestamp); - updateTimeline(tuple, resultDiff); - - return resultDiff; - } - - protected Diff putLazy(final Tuple tuple, final Timestamp timestamp) { - final CumulativeCounter counter = getCounter(tuple, timestamp); - counter.diff += 1; - // before the INSERT timestamp, no change at all - // it cannot happen that those became positive in this round - addFoldingState(tuple, new FoldingState(+1), timestamp); - return EMPTY_DIFF; - } - - protected Diff removeEager(final Tuple tuple, final Timestamp timestamp) { - final Diff resultDiff = new Diff<>(); - final CumulativeCounter counter = getCounter(tuple, timestamp); - --counter.diff; - - // before the DELETE timestamp, no change at all - // it cannot happen that those became zero in this round - SignChange signChangeAtPrevious = SignChange.IRRELEVANT; - - final NavigableMap nextCounters = this.counters.get(tuple).tailMap(timestamp, - true); - for (final Entry currentEntry : nextCounters.entrySet()) { - final Timestamp currentTimestamp = currentEntry.getKey(); - final CumulativeCounter currentCounter = currentEntry.getValue(); - --currentCounter.cumulative; - signChangeAtPrevious = computeDiffsEager(Direction.DELETE, currentCounter, signChangeAtPrevious, - currentTimestamp, resultDiff); - } - - gcCounters(counter, tuple, timestamp); - updateTimeline(tuple, resultDiff); - - return resultDiff; - } - - protected Diff removeLazy(final Tuple tuple, final Timestamp timestamp) { - final CumulativeCounter counter = getCounter(tuple, timestamp); - counter.diff -= 1; - // before the DELETE timestamp, no change at all - // it cannot happen that those became zero in this round - addFoldingState(tuple, new FoldingState(-1), timestamp); - return EMPTY_DIFF; - } - - /** - * Updates and garbage collects the timeline of the given tuple based on the given timeline diff. - */ - protected void updateTimeline(final Tuple tuple, final Diff diff) { - if (!diff.isEmpty()) { - this.timelines.compute(tuple, (k, oldTimeline) -> { - this.presentAtInfinity.remove(tuple); - final Timeline timeline = oldTimeline == null ? Timelines.createFrom(diff) - : oldTimeline.mergeAdditive(diff); - if (timeline.isPresentAtInfinity()) { - this.presentAtInfinity.add(tuple); - } - if (timeline.isEmpty()) { - return null; - } else { - return timeline; - } - }); - } - } - - /** - * @since 2.8 - */ - public Set getTuplesAtInfinity() { - return this.presentAtInfinity; - } - - /** - * Returns the number of tuples that are present at the moment 'infinity'. - */ - public int getCountAtInfinity() { - return this.presentAtInfinity.size(); - } - - /** - * Returns true if the given tuple is present at the moment 'infinity'. - */ - public boolean isPresentAtInfinity(final Tuple tuple) { - final Timeline timeline = this.timelines.get(tuple); - if (timeline == null) { - return false; - } else { - return timeline.isPresentAtInfinity(); - } - } - - public boolean isEmpty() { - return this.counters.isEmpty(); - } - - public int size() { - return this.counters.size(); - } - - public Set keySet() { - return this.counters.keySet(); - } - - public Map> asMap() { - return this.timelines; - } - - public Timeline get(final ITuple tuple) { - return this.timelines.get(tuple); - } - - @Override - public void clear() { - this.counters.clear(); - this.timelines.clear(); - if (this.foldingState != null) { - this.foldingState.clear(); - } - } - - public boolean containsKey(final ITuple tuple) { - return this.counters.containsKey(tuple); - } - - @Override - public String toString() { - return this.counters + "\n" + this.timelines + "\n" + this.foldingState + "\n"; - } - - protected static final class CumulativeCounter { - protected int diff; - protected int cumulative; - - protected CumulativeCounter(final int diff, final int cumulative) { - this.diff = diff; - this.cumulative = cumulative; - } - - @Override - public String toString() { - return "{diff=" + this.diff + ", cumulative=" + this.cumulative + "}"; - } - - } - - protected static final class FoldingState { - protected final int diff; - - protected FoldingState(final int diff) { - this.diff = diff; - } - - @Override - public String toString() { - return "{diff=" + this.diff + "}"; - } - - /** - * The returned result will never be null, even if the resulting diff is zero. - */ - public FoldingState merge(final FoldingState that) { - Preconditions.checkArgument(that != null); - return new FoldingState(this.diff + that.diff); - } - - } - - protected enum SignChange { - BECAME_POSITIVE, BECAME_ZERO, IRRELEVANT; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java deleted file mode 100644 index ea70e61d..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util.resumable; - -import java.util.Map; - -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; - -/** - * A masked {@link Resumable} implementation, which maintains lazy folding per tuple signature. - * - * @author Tamas Szabo - * @since 2.4 - */ -public interface MaskedResumable> extends Resumable { - - /** - * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to - * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more - * folding to do towards higher timestamps. - */ - public Map>> resumeAt(final Timestamp timestamp); - - /** - * Returns the set of signatures for which lazy folding shall be resumed at the next timestamp. - */ - public Iterable getResumableSignatures(); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java deleted file mode 100644 index 2861df20..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util.resumable; - -/** - * A resumable lazily folds its state towards higher timestamps. Folding shall be done in the increasing order of - * timestamps, and it shall be interrupted after each step. The resumable can then be instructed to resume the folding, - * one step at a time. - * - * @author Tamas Szabo - * @since 2.4 - */ -public interface Resumable> { - - /** - * Returns the smallest timestamp where lazy folding shall be resumed, or null if there is no more folding to do in this - * resumable. - */ - public Timestamp getResumableTimestamp(); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java deleted file mode 100644 index 1671940b..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util.resumable; - -import java.util.Map; - -import tools.refinery.viatra.runtime.matchers.tuple.Tuple; -import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; - -/** - * A unmasked {@link Resumable} implementation, which maintains lazy folding without caring about tuple signatures. - * - * @author Tamas Szabo - * @since 2.4 - */ -public interface UnmaskedResumable> extends Resumable { - - /** - * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to - * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more - * folding to do towards higher timestamps. - */ - public Map> resumeAt(final Timestamp timestamp); - - /** - * Returns the set of tuples for which lazy folding shall be resumed at the next timestamp. - */ - public Iterable getResumableTuples(); - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java deleted file mode 100644 index 0532d094..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java +++ /dev/null @@ -1,111 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util.timeline; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import tools.refinery.viatra.runtime.matchers.util.Direction; -import tools.refinery.viatra.runtime.matchers.util.Signed; - -/** - * A compact timeline may cosist of an arbitrary amount of moments. - * It is backed by an {@link ArrayList}. - * - * @author Tamas Szabo - * @since 2.4 - */ -public class CompactTimeline> extends Timeline { - - protected final List elements; - - CompactTimeline() { - this.elements = new ArrayList<>(); - } - - CompactTimeline(final Timestamp timestamp) { - this(); - this.elements.add(timestamp); - } - - CompactTimeline(final List timestamps) { - this.elements = new ArrayList<>(timestamps.size()); - this.elements.addAll(timestamps); - } - - CompactTimeline(final Diff diff) { - this.elements = new ArrayList<>(diff.size()); - Direction expected = Direction.INSERT; - for (Signed signed : diff) { - if (!expected.equals(signed.getDirection())) { - throw new IllegalStateException(String.format("Expected direction (%s) constraint violated! %s @%s", - expected, diff, signed.getPayload())); - } - this.elements.add(signed.getPayload()); - expected = expected.opposite(); - } - } - - @Override - public Signed getSigned(final int index) { - final Direction direction = index % 2 == 0 ? Direction.INSERT : Direction.DELETE; - return new Signed<>(direction, this.getUnsigned(index)); - } - - @Override - public Timestamp getUnsigned(final int index) { - if (this.elements.size() <= index) { - throw new IllegalArgumentException( - "Timeline size (" + this.size() + ") is smaller than requested index " + index + "!"); - } else { - return this.elements.get(index); - } - } - - @Override - public int size() { - return this.elements.size(); - } - - @Override - public boolean isPresentAtInfinity() { - // if it has an odd length, then it ends with "INSERT" - return this.size() % 2 == 1; - } - - @Override - public Iterable> asChangeSequence() { - Iterable outer = this.elements; - return () -> { - final Iterator itr = outer.iterator(); - return new Iterator>() { - Direction direction = Direction.INSERT; - - @Override - public boolean hasNext() { - return itr.hasNext(); - } - - @Override - public Signed next() { - final Signed result = new Signed(direction, itr.next()); - direction = direction.opposite(); - return result; - } - }; - }; - } - - @Override - public boolean isEmpty() { - return this.elements.isEmpty(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java deleted file mode 100644 index cec6049e..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util.timeline; - -import java.util.ArrayList; - -import tools.refinery.viatra.runtime.matchers.util.Signed; - -/** - * The description of a delta that specifies how a {@link Timeline} changes. It consists of {@link Signed} timestamps that - * depict the moments of insertions and deletions on the timeline. - * - * @author Tamas Szabo - * @since 2.4 - * @param - * the type representing the timestamps - */ -public class Diff> extends ArrayList> { - - private static final long serialVersionUID = 3853460426655994160L; - - public Diff() { - - } - - public void appendWithCancellation(Signed item) { - if (this.isEmpty()) { - this.add(item); - } else { - final Signed last = this.get(this.size() - 1); - final int lastMinusItem = last.getPayload().compareTo(item.getPayload()); - if (lastMinusItem == 0) { - if (last.getDirection() != item.getDirection()) { - // cancellation - this.remove(this.size() - 1); - } else { - throw new IllegalStateException( - "Trying to insert or delete for the second time at the same timestamp! " + item); - } - } else if (lastMinusItem > 0) { - throw new IllegalStateException( - "Trying to append a timestamp that is smaller than the last one! " + last + " " + item); - } else { - this.add(item); - } - } - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java deleted file mode 100644 index 526a95f5..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java +++ /dev/null @@ -1,73 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util.timeline; - -import java.util.Collections; - -import tools.refinery.viatra.runtime.matchers.util.Direction; -import tools.refinery.viatra.runtime.matchers.util.Signed; - -/** - * A timeline which solely consists of one timestamp value, representing a single insertion. Intuitively, a singleton - * timeline always represents a bump which starts at the given timestamp and lasts till plus infinity. - * - * @author Tamas Szabo - * @since 2.4 - */ -public class SingletonTimeline> extends Timeline { - - protected final Timestamp start; - - SingletonTimeline(final Timestamp timestamp) { - this.start = timestamp; - } - - SingletonTimeline(final Diff diff) { - if (diff.size() != 1 || diff.get(0).getDirection() == Direction.DELETE) { - throw new IllegalArgumentException("There is only a single (insert) timestamp in the singleton timestamp!"); - } else { - this.start = diff.get(0).getPayload(); - } - } - - @Override - public Signed getSigned(final int index) { - return new Signed<>(Direction.INSERT, this.getUnsigned(index)); - } - - @Override - public Timestamp getUnsigned(final int index) { - if (index != 0) { - throw new IllegalArgumentException("There is only a single (insert) timestamp in the singleton timestamp!"); - } else { - return this.start; - } - } - - @Override - public int size() { - return 1; - } - - @Override - public boolean isPresentAtInfinity() { - return true; - } - - @Override - public Iterable> asChangeSequence() { - return Collections.singletonList(this.getSigned(0)); - } - - @Override - public boolean isEmpty() { - return false; - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java deleted file mode 100644 index 9214536c..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util.timeline; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import tools.refinery.viatra.runtime.matchers.util.Direction; -import tools.refinery.viatra.runtime.matchers.util.Signed; - -/** - * A timeline describes the life cycle of a piece of data (typically a tuple in a relation) as a sequence of moments. - * Even moments represent appearances, odd moments represent disappearances. A timeline is immutable, once created, it - * is not possible to extend it with further moments. - * - * @author Tamas Szabo - * @since 2.4 - */ -public abstract class Timeline> { - - public abstract Iterable> asChangeSequence(); - - public abstract boolean isPresentAtInfinity(); - - public abstract boolean isEmpty(); - - public abstract int size(); - - public abstract Signed getSigned(final int index); - - public abstract Timestamp getUnsigned(final int index); - - public Timeline mergeMultiplicative(final Timeline that) { - final List result = new ArrayList<>(); - int thisIdx = 0, thatIdx = 0; - Timestamp thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; - Timestamp thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; - - while (thisNext != null || thatNext != null) { - int thisMinusThat = 0; - if (thisNext != null && thatNext != null) { - thisMinusThat = thisNext.compareTo(thatNext); - } - if (thisNext == null || thisMinusThat > 0) { - if (thisIdx % 2 == 1) { - result.add(thatNext); - } - thatIdx++; - thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; - } else if (thatNext == null || thisMinusThat < 0) { - if (thatIdx % 2 == 1) { - result.add(thisNext); - } - thisIdx++; - thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; - } else { - if (thisIdx % 2 == thatIdx % 2) { - result.add(thisNext); - } - thisIdx++; - thatIdx++; - thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; - thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; - } - } - - return Timelines.createFrom(result); - } - - /** - * Merges this timeline with the given timestamp diff. The expectation is that the resulting timeline starts with an - * insertion. The logic is similar to a merge sort; we iterate side-by-side over the timeline and the diff. During - * the merge, cancellation can happen if at the same timestamp we observe different signs at the corresponding - * timeline and diff elements. - */ - public Timeline mergeAdditive(final Diff diff) { - final Iterator> thisItr = this.asChangeSequence().iterator(); - final Iterator> diffItr = diff.iterator(); - final List result = new ArrayList<>(); - Direction expected = Direction.INSERT; - Signed thisNext = thisItr.hasNext() ? thisItr.next() : null; - Signed diffNext = diffItr.hasNext() ? diffItr.next() : null; - - while (thisNext != null || diffNext != null) { - int thisMinusDiff = 0; - if (thisNext != null && diffNext != null) { - thisMinusDiff = thisNext.getPayload().compareTo(diffNext.getPayload()); - } - - if (thisNext == null || thisMinusDiff > 0) { - if (!expected.equals(diffNext.getDirection())) { - throw new IllegalStateException( - String.format("Expected direction (%s) constraint violated! %s %s @%s", expected, this, - diff, diffNext.getPayload())); - } - result.add(diffNext.getPayload()); - diffNext = diffItr.hasNext() ? diffItr.next() : null; - expected = expected.opposite(); - } else if (diffNext == null || thisMinusDiff < 0) { - if (!expected.equals(thisNext.getDirection())) { - throw new IllegalStateException( - String.format("Expected direction (%s) constraint violated! %s %s @%s", expected, this, - diff, thisNext.getPayload())); - } - result.add(thisNext.getPayload()); - thisNext = thisItr.hasNext() ? thisItr.next() : null; - expected = expected.opposite(); - } else { - // they cancel out each other - if (diffNext.getDirection().equals(thisNext.getDirection())) { - throw new IllegalStateException(String.format("Changes do not cancel out each other! %s %s @%s", - this, diff, thisNext.getPayload())); - } - diffNext = diffItr.hasNext() ? diffItr.next() : null; - thisNext = thisItr.hasNext() ? thisItr.next() : null; - } - } - - return Timelines.createFrom(result); - } - - @Override - public String toString() { - final StringBuilder builder = new StringBuilder(); - builder.append("["); - boolean first = true; - for (final Signed element : this.asChangeSequence()) { - if (first) { - first = false; - } else { - builder.append(", "); - } - builder.append(element.toString()); - } - builder.append("]"); - return builder.toString(); - } - -} diff --git a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java b/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java deleted file mode 100644 index 747fda15..00000000 --- a/subprojects/viatra-runtime-matchers/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java +++ /dev/null @@ -1,46 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.matchers.util.timeline; - -import java.util.List; - -/** - * Utility class for creating {@link Timeline}s. - * @author Tamas Szabo - * @since 2.4 - */ -public final class Timelines> { - - public static > Timeline createEmpty() { - return new CompactTimeline(); - } - - public static > Timeline createFrom( - final Diff diffs) { - if (diffs.size() == 1) { - return new SingletonTimeline(diffs); - } else { - return new CompactTimeline(diffs); - } - } - - public static > Timeline createFrom( - final List timestamps) { - if (timestamps.size() == 1) { - return new SingletonTimeline(timestamps.get(0)); - } else { - return new CompactTimeline(timestamps); - } - } - - public static > Timeline createFrom(final Timestamp timestamp) { - return new SingletonTimeline(timestamp); - } - -} diff --git a/subprojects/viatra-runtime-rete-recipes/build.gradle.kts b/subprojects/viatra-runtime-rete-recipes/build.gradle.kts index 1f33e2f9..b1b11b4e 100644 --- a/subprojects/viatra-runtime-rete-recipes/build.gradle.kts +++ b/subprojects/viatra-runtime-rete-recipes/build.gradle.kts @@ -13,7 +13,7 @@ plugins { } dependencies { - api(project(":refinery-viatra-runtime-matchers")) + api(project(":refinery-viatra-runtime")) api(libs.ecore) mwe2(libs.ecore.codegen) mwe2(libs.mwe.utils) diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java index a9863400..19e02f10 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/aggregation/timely/FaithfulParallelTimelyColumnAggregatorNode.java @@ -3,36 +3,31 @@ * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. - * + * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package tools.refinery.viatra.runtime.rete.aggregation.timely; -import java.util.Collections; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.TreeMap; - import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; import tools.refinery.viatra.runtime.matchers.tuple.Tuple; import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.Direction; -import tools.refinery.viatra.runtime.matchers.util.IDeltaBag; -import tools.refinery.viatra.runtime.matchers.util.Preconditions; -import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.*; import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode.CumulativeAggregate; import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulParallelTimelyColumnAggregatorNode.FoldingState; -import tools.refinery.viatra.runtime.rete.aggregation.timely.FaithfulTimelyColumnAggregatorNode.MergeableFoldingState; import tools.refinery.viatra.runtime.rete.network.ReteContainer; import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; import tools.refinery.viatra.runtime.rete.network.communication.timely.ResumableNode; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.TreeMap; + /** * Faithful column aggregator with parallel aggregation architecture. - * + * * @author Tamas Szabo * @since 2.4 * @@ -214,4 +209,4 @@ public class FaithfulParallelTimelyColumnAggregatorNode * Usually, the mailbox performs counting of messages so that they can cancel each other out. However, if marked as a * fall-through mailbox, than update messages are delivered directly to the receiver node to reduce overhead. - * + * * @author Tamas Szabo * @since 2.0 */ diff --git a/subprojects/viatra-runtime/build.gradle.kts b/subprojects/viatra-runtime/build.gradle.kts index b1e65c94..20a8c2c3 100644 --- a/subprojects/viatra-runtime/build.gradle.kts +++ b/subprojects/viatra-runtime/build.gradle.kts @@ -9,7 +9,7 @@ plugins { } dependencies { - api(project(":refinery-viatra-runtime-matchers")) - api(project(":refinery-viatra-runtime-base-itc")) implementation(libs.slf4j.log4j) + implementation(libs.eclipseCollections.api) + implementation(libs.eclipseCollections) } diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java index ebae57a7..58285845 100644 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java @@ -9,7 +9,6 @@ package tools.refinery.viatra.runtime.api; -import org.eclipse.emf.common.notify.Notifier; import tools.refinery.viatra.runtime.api.scope.IBaseIndex; import tools.refinery.viatra.runtime.api.scope.QueryScope; import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java new file mode 100644 index 00000000..d0367cde --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.counting; + +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; +import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; +import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; +import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; + +/** + * This class is the optimized implementation of the Counting algorithm. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the nodes in the graph data source + */ +public class CountingAlg implements IGraphObserver, ITcDataSource { + + private CountingTcRelation tc = null; + private IBiDirectionalGraphDataSource gds = null; + private List> observers; + + /** + * Constructs a new Counting algorithm and initializes the transitive closure relation with the given graph data + * source. Attach itself on the graph data source as an observer. + * + * @param gds + * the graph data source instance + */ + public CountingAlg(IGraphDataSource gds) { + + if (gds instanceof IBiDirectionalGraphDataSource) { + this.gds = (IBiDirectionalGraphDataSource) gds; + } else { + this.gds = new IBiDirectionalWrapper(gds); + } + + observers = CollectionsFactory.>createObserverList(); + tc = new CountingTcRelation(true); + + initTc(); + gds.attachObserver(this); + } + + /** + * Initializes the transitive closure relation. + */ + private void initTc() { + this.setTcRelation(CountingTcRelation.createFrom(gds)); + } + + @Override + public void edgeInserted(V source, V target) { + if (!source.equals(target)) { + deriveTc(source, target, true); + } + } + + @Override + public void edgeDeleted(V source, V target) { + if (!source.equals(target)) { + deriveTc(source, target, false); + } + } + + @Override + public void nodeInserted(V n) { + + } + + @Override + public void nodeDeleted(V n) { + this.tc.deleteTupleEnd(n); + } + + /** + * Derives the transitive closure relation when an edge is inserted or deleted. + * + * @param source + * the source of the edge + * @param target + * the target of the edge + * @param dCount + * the value is -1 if an edge was deleted and +1 if an edge was inserted + */ + private void deriveTc(V source, V target, boolean isInsertion) { + + // if (dCount == 1 && isReachable(target, source)) { + // System.out.println("The graph contains cycle with (" + source + ","+ target + ") edge!"); + // } + + CountingTcRelation dtc = new CountingTcRelation(false); + Set tupEnds = null; + + // 1. d(tc(x,y)) :- d(l(x,y)) + if (tc.updateTuple(source, target, isInsertion)) { + dtc.updateTuple(source, target, true /* deltas implicitly have the same sign as isInsertion*/); + notifyTcObservers(source, target, isInsertion); + } + + // 2. d(tc(x,y)) :- d(l(x,z)) & tc(z,y) + tupEnds = tc.getTupleEnds(target); + if (tupEnds != null) { + for (V tupEnd : tupEnds) { + if (!tupEnd.equals(source)) { + if (tc.updateTuple(source, tupEnd, isInsertion)) { + dtc.updateTuple(source, tupEnd, true /* deltas implicitly have the same sign as isInsertion*/); + notifyTcObservers(source, tupEnd, isInsertion); + } + } + } + } + + // 3. d(tc(x,y)) :- lv(x,z) & d(tc(z,y)) + CountingTcRelation newTuples = dtc; + CountingTcRelation tmp = null; + dtc = new CountingTcRelation(false); + + IMemoryView nodes = null; + + while (!newTuples.isEmpty()) { + + tmp = dtc; + dtc = newTuples; + newTuples = tmp; + newTuples.clear(); + + for (V tS : dtc.getTupleStarts()) { + nodes = gds.getSourceNodes(tS); + for (V nS : nodes.distinctValues()) { + int count = nodes.getCount(nS); + for (int i = 0; i < count; i++) { + tupEnds = dtc.getTupleEnds(tS); + if (tupEnds != null) { + for (V tT : tupEnds) { + if (!nS.equals(tT)) { + if (tc.updateTuple(nS, tT, isInsertion)) { + newTuples.updateTuple(nS, tT, true /* deltas implicitly have the same sign as isInsertion*/); + notifyTcObservers(nS, tT, isInsertion); + } + } + } + } + } + } + } + } + + // System.out.println(tc); + } + + public ITcRelation getTcRelation() { + return this.tc; + } + + public void setTcRelation(CountingTcRelation tc) { + this.tc = tc; + } + + @Override + public boolean isReachable(V source, V target) { + return tc.containsTuple(source, target); + } + + @Override + public void attachObserver(ITcObserver to) { + this.observers.add(to); + + } + + @Override + public void detachObserver(ITcObserver to) { + this.observers.remove(to); + } + + @Override + public Set getAllReachableTargets(V source) { + return tc.getTupleEnds(source); + } + + @Override + public Set getAllReachableSources(V target) { + return tc.getTupleStarts(target); + } + + private void notifyTcObservers(V source, V target, boolean isInsertion) { + if (isInsertion) { + for (ITcObserver o : observers) { + o.tupleInserted(source, target); + } + } else { + for (ITcObserver o : observers) { + o.tupleDeleted(source, target); + } + } + } + + @Override + public void dispose() { + tc.clear(); + this.gds.detachObserver(this); + } + + @Override + public IGraphPathFinder getPathFinder() { + return new DFSPathFinder(gds, this); + } +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java new file mode 100644 index 00000000..44abbc3d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java @@ -0,0 +1,259 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.counting; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting; +import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; + +/** + * Transitive closure relation implementation for the Counting algorithm. + * + * @author Tamas Szabo + * + * @param + */ +public class CountingTcRelation implements ITcRelation { + + private IMultiLookup tuplesForward = null; + private IMultiLookup tuplesBackward = null; + + protected CountingTcRelation(boolean backwardIndexing) { + tuplesForward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); + if (backwardIndexing) + tuplesBackward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); + } + + protected boolean isEmpty() { + return 0 == this.tuplesForward.countKeys(); + } + + protected void clear() { + this.tuplesForward.clear(); + + if (tuplesBackward != null) { + this.tuplesBackward.clear(); + } + } + + protected void union(CountingTcRelation rA) { + IMultiLookup rForward = rA.tuplesForward; + for (V source : rForward.distinctKeys()) { + IMemoryView targetBag = rForward.lookup(source); + for (V target : targetBag.distinctValues()) { + this.addTuple(source, target, targetBag.getCount(target)); + } + } + } + + public int getCount(V source, V target) { + IMemoryView bucket = tuplesForward.lookup(source); + return bucket == null ? 0 : bucket.getCount(target); + } + + /** + * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false + * otherwise (in this case count is incremented with the given count parameter). + * + * @param source + * the source of the tuple + * @param target + * the target of the tuple + * @param count + * the count of the tuple, must be positive + * @return true if the relation did not contain previously the tuple + */ + public boolean addTuple(V source, V target, int count) { + if (tuplesBackward != null) { + tuplesBackward.addPairPositiveMultiplicity(target, source, count); + } + + ChangeGranularity change = + tuplesForward.addPairPositiveMultiplicity(source, target, count); + + return change != ChangeGranularity.DUPLICATE; + } + + /** + * Derivation count of the tuple (source,target) is incremented or decremented. + * Returns true iff updated to / from zero derivation count. + * @since 1.7 + */ + public boolean updateTuple(V source, V target, boolean isInsertion) { + if (isInsertion) { + if (tuplesBackward != null) { + tuplesBackward.addPair(target, source); + } + ChangeGranularity change = + tuplesForward.addPair(source, target); + return change != ChangeGranularity.DUPLICATE; + } else { + if (tuplesBackward != null) { + tuplesBackward.removePair(target, source); + } + ChangeGranularity change = + tuplesForward.removePair(source, target); + return change != ChangeGranularity.DUPLICATE; + } + } + + public void deleteTupleEnd(V deleted) { + Set sourcesToDelete = CollectionsFactory.createSet(); + Set targetsToDelete = CollectionsFactory.createSet(); + + for (V target : tuplesForward.lookupOrEmpty(deleted).distinctValues()) { + targetsToDelete.add(target); + } + if (tuplesBackward != null) { + for (V source : tuplesBackward.lookupOrEmpty(deleted).distinctValues()) { + sourcesToDelete.add(source); + } + } else { + for (V sourceCandidate : tuplesForward.distinctKeys()) { + if (tuplesForward.lookupOrEmpty(sourceCandidate).containsNonZero(deleted)) + sourcesToDelete.add(sourceCandidate); + } + } + + for (V source : sourcesToDelete) { + int count = tuplesForward.lookupOrEmpty(source).getCount(deleted); + for (int i=0; i< count; ++i) tuplesForward.removePair(source, deleted); + } + for (V target : targetsToDelete) { + int count = tuplesForward.lookupOrEmpty(deleted).getCount(target); + for (int i=0; i< count; ++i) tuplesForward.removePair(deleted, target); + } + + if (tuplesBackward != null) { + for (V source : sourcesToDelete) { + int count = tuplesBackward.lookupOrEmpty(deleted).getCount(source); + for (int i=0; i< count; ++i) tuplesBackward.removePair(deleted, source); + } + for (V target : targetsToDelete) { + int count = tuplesBackward.lookupOrEmpty(target).getCount(deleted); + for (int i=0; i< count; ++i) tuplesBackward.removePair(target, deleted); + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("TcRelation = "); + + for (V source : tuplesForward.distinctKeys()) { + IMemoryView targets = tuplesForward.lookup(source); + for (V target : targets.distinctValues()) { + sb.append("{(" + source + "," + target + ")," + targets.getCount(target) + "} "); + } + } + + return sb.toString(); + } + + @Override + public Set getTupleEnds(V source) { + IMemoryView tupEnds = tuplesForward.lookup(source); + if (tupEnds == null) + return null; + return tupEnds.distinctValues(); + } + + /** + * Returns the set of nodes from which the target node is reachable, if already computed. + * + * @param target + * the target node + * @return the set of source nodes + * @throws UnsupportedOperationException if backwards index not computed + */ + public Set getTupleStarts(V target) { + if (tuplesBackward != null) { + IMemoryView tupStarts = tuplesBackward.lookup(target); + if (tupStarts == null) + return null; + return tupStarts.distinctValues(); + } else { + throw new UnsupportedOperationException("built without backward indexing"); + } + } + + @Override + public Set getTupleStarts() { + Set nodes = CollectionsFactory.createSet(); + for (V s : tuplesForward.distinctKeys()) { + nodes.add(s); + } + return nodes; + } + + /** + * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise. + * + * @param source + * the source node + * @param target + * the target node + * @return true if tuple is present, false otherwise + */ + public boolean containsTuple(V source, V target) { + return tuplesForward.lookupOrEmpty(source).containsNonZero(target); + } + + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null || this.getClass() != obj.getClass()) { + return false; + } else { + CountingTcRelation aTR = (CountingTcRelation) obj; + + return tuplesForward.equals(aTR.tuplesForward); + } + } + + @Override + public int hashCode() { + return tuplesForward.hashCode(); + } + + public static CountingTcRelation createFrom(IBiDirectionalGraphDataSource gds) { + List topologicalSorting = TopologicalSorting.compute(gds); + CountingTcRelation tc = new CountingTcRelation(true); + Collections.reverse(topologicalSorting); + for (V n : topologicalSorting) { + IMemoryView sourceNodes = gds.getSourceNodes(n); + Set tupEnds = tc.getTupleEnds(n); + for (V s : sourceNodes.distinctValues()) { + int count = sourceNodes.getCount(s); + for (int i = 0; i < count; i++) { + tc.updateTuple(s, n, true); + if (tupEnds != null) { + for (V t : tupEnds) { + tc.updateTuple(s, t, true); + } + } + } + } + } + + return tc; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java new file mode 100644 index 00000000..b92d08bf --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java @@ -0,0 +1,308 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.dred; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; +import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; +import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple; +import tools.refinery.viatra.runtime.base.itc.alg.misc.dfs.DFSAlg; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; +import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; + +/** + * This class is the optimized implementation of the DRED algorithm. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the nodes in the graph data source + */ +public class DRedAlg implements IGraphObserver, ITcDataSource { + + private IGraphDataSource graphDataSource = null; + private DRedTcRelation tc = null; + private DRedTcRelation dtc = null; + private List> observers; + + /** + * Constructs a new DRED algorithm and initializes the transitive closure relation with the given graph data source. + * Attach itself on the graph data source as an observer. + * + * @param gds + * the graph data source instance + */ + public DRedAlg(IGraphDataSource gds) { + this.observers = new ArrayList>(); + this.graphDataSource = gds; + this.tc = new DRedTcRelation(); + this.dtc = new DRedTcRelation(); + initTc(); + graphDataSource.attachObserver(this); + } + + /** + * Constructs a new DRED algorithm and initializes the transitive closure relation with the given relation. Attach + * itself on the graph data source as an observer. + * + * @param gds + * the graph data source instance + * @param tc + * the transitive closure instance + */ + public DRedAlg(IGraphDataSource gds, DRedTcRelation tc) { + this.graphDataSource = gds; + this.tc = tc; + this.dtc = new DRedTcRelation(); + graphDataSource.attachObserver(this); + } + + /** + * Initializes the transitive closure relation. + */ + private void initTc() { + DFSAlg dfsa = new DFSAlg(this.graphDataSource); + this.setTcRelation(dfsa.getTcRelation()); + this.graphDataSource.detachObserver(dfsa); + } + + @Override + public void edgeInserted(V source, V target) { + if (!source.equals(target)) { + Set tupStarts = null; + Set tupEnds = null; + Set> tuples = new HashSet>(); + + if (!source.equals(target) && tc.addTuple(source, target)) { + tuples.add(new Tuple(source, target)); + } + + // 2. d+(tc(x,y)) :- d+(tc(x,z)) & lv(z,y) Descartes product + tupStarts = tc.getTupleStarts(source); + tupEnds = tc.getTupleEnds(target); + + for (V s : tupStarts) { + for (V t : tupEnds) { + if (!s.equals(t) && tc.addTuple(s, t)) { + tuples.add(new Tuple(s, t)); + } + } + } + + // (s, source) -> (source, target) + // tupStarts = tc.getTupleStarts(source); + for (V s : tupStarts) { + if (!s.equals(target) && tc.addTuple(s, target)) { + tuples.add(new Tuple(s, target)); + } + } + + // (source, target) -> (target, t) + // tupEnds = tc.getTupleEnds(target); + for (V t : tupEnds) { + if (!source.equals(t) && tc.addTuple(source, t)) { + tuples.add(new Tuple(source, t)); + } + } + + notifyTcObservers(tuples, 1); + } + } + + @Override + public void edgeDeleted(V source, V target) { + if (!source.equals(target)) { + + // Computing overestimate, Descartes product of A and B sets, where + // A: those nodes from which source is reachable + // B: those nodes which is reachable from target + + Map, Integer> tuples = new HashMap, Integer>(); + Set sources = tc.getTupleStarts(source); + Set targets = tc.getTupleEnds(target); + + tc.removeTuple(source, target); + tuples.put(new Tuple(source, target), -1); + + for (V s : sources) { + for (V t : targets) { + if (!s.equals(t)) { + tc.removeTuple(s, t); + tuples.put(new Tuple(s, t), -1); + } + } + } + + for (V s : sources) { + if (!s.equals(target)) { + tc.removeTuple(s, target); + tuples.put(new Tuple(s, target), -1); + } + } + + for (V t : targets) { + if (!source.equals(t)) { + tc.removeTuple(source, t); + tuples.put(new Tuple(source, t), -1); + } + } + + // System.out.println("overestimate: "+dtc); + + // Modify overestimate with those tuples that have alternative derivations + // 1. q+(tc(x,y)) :- lv(x,y) + for (V s : graphDataSource.getAllNodes()) { + IMemoryView targetNodes = graphDataSource.getTargetNodes(s); + for (Entry entry : targetNodes.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V t = entry.getKey(); + if (!s.equals(t)) { + tc.addTuple(s, t); + Tuple tuple = new Tuple(s, t); + Integer count = tuples.get(tuple); + if (count != null && count == -1) { + tuples.remove(tuple); + } + } + + } + } + } + + // 2. q+(tc(x,y)) :- tcv(x,z) & lv(z,y) + DRedTcRelation newTups = new DRedTcRelation(); + dtc.clear(); + dtc.union(tc); + + while (!dtc.isEmpty()) { + + newTups.clear(); + newTups.union(dtc); + dtc.clear(); + + for (V s : newTups.getTupleStarts()) { + for (V t : newTups.getTupleEnds(s)) { + IMemoryView targetNodes = graphDataSource.getTargetNodes(t); + if (targetNodes != null) { + for (Entry entry : targetNodes.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V tn = entry.getKey(); + if (!s.equals(tn) && tc.addTuple(s, tn)) { + dtc.addTuple(s, tn); + tuples.remove(new Tuple(s, tn)); + } + } + } + } + } + } + } + + notifyTcObservers(tuples.keySet(), -1); + } + } + + @Override + public void nodeInserted(V n) { + // Node inserted does not result new tc tuple. + } + + @Override + public void nodeDeleted(V n) { + // FIXME node deletion may involve the deletion of incoming and outgoing edges too + Set set = tc.getTupleEnds(n); + Set modSet = null; + + // n -> target + modSet = new HashSet(set); + + for (V tn : modSet) { + this.tc.removeTuple(n, tn); + } + + // source -> n + set = tc.getTupleStarts(n); + + modSet = new HashSet(set); + + for (V sn : modSet) { + this.tc.removeTuple(sn, n); + } + } + + public DRedTcRelation getTcRelation() { + return this.tc; + } + + public void setTcRelation(DRedTcRelation tc) { + this.tc = tc; + } + + @Override + public boolean isReachable(V source, V target) { + return tc.containsTuple(source, target); + } + + @Override + public void attachObserver(ITcObserver to) { + this.observers.add(to); + } + + @Override + public void detachObserver(ITcObserver to) { + this.observers.remove(to); + } + + @Override + public Set getAllReachableTargets(V source) { + return tc.getTupleEnds(source); + } + + @Override + public Set getAllReachableSources(V target) { + return tc.getTupleStarts(target); + } + + protected void notifyTcObservers(Set> tuples, int dir) { + for (ITcObserver o : observers) { + for (Tuple t : tuples) { + if (!t.getSource().equals(t.getTarget())) { + if (dir == 1) { + o.tupleInserted(t.getSource(), t.getTarget()); + } + if (dir == -1) { + o.tupleDeleted(t.getSource(), t.getTarget()); + } + } + } + } + } + + @Override + public void dispose() { + tc = null; + dtc = null; + } + + @Override + public IGraphPathFinder getPathFinder() { + return new DFSPathFinder(graphDataSource, this); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java new file mode 100644 index 00000000..8543b79c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java @@ -0,0 +1,223 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.dred; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; + +public class DRedTcRelation implements ITcRelation { + + // tc(a,b) means that b is transitively reachable from a + private Map> tuplesForward; + + // data structure to efficiently get those nodes from which a given node is reachable + // symmetric to tuplesForward + private Map> tuplesBackward; + + public DRedTcRelation() { + this.tuplesForward = new HashMap>(); + this.tuplesBackward = new HashMap>(); + } + + public void clear() { + this.tuplesForward.clear(); + this.tuplesBackward.clear(); + } + + public boolean isEmpty() { + return tuplesForward.isEmpty(); + } + + public void removeTuple(V source, V target) { + + // removing tuple from 'forward' tc relation + Set sSet = tuplesForward.get(source); + if (sSet != null) { + sSet.remove(target); + if (sSet.size() == 0) + tuplesForward.remove(source); + } + + // removing tuple from 'backward' tc relation + Set tSet = tuplesBackward.get(target); + if (tSet != null) { + tSet.remove(source); + if (tSet.size() == 0) + tuplesBackward.remove(target); + } + } + + /** + * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false + * otherwise. + * + * @param source + * the source of the tuple + * @param target + * the target of the tuple + * @return true if the relation did not contain previously the tuple + */ + public boolean addTuple(V source, V target) { + + // symmetric modification, it is sufficient to check the return value in one collection + // adding tuple to 'forward' tc relation + Set sSet = tuplesForward.get(source); + if (sSet == null) { + Set newSet = new HashSet(); + newSet.add(target); + tuplesForward.put(source, newSet); + } else { + sSet.add(target); + } + + // adding tuple to 'backward' tc relation + Set tSet = tuplesBackward.get(target); + if (tSet == null) { + Set newSet = new HashSet(); + newSet.add(source); + tuplesBackward.put(target, newSet); + return true; + } else { + boolean ret = tSet.add(source); + return ret; + } + + } + + /** + * Union operation of two tc realtions. + * + * @param rA + * the other tc relation + */ + public void union(DRedTcRelation rA) { + for (V source : rA.tuplesForward.keySet()) { + for (V target : rA.tuplesForward.get(source)) { + this.addTuple(source, target); + } + } + } + + /** + * Computes the difference of this tc relation and the given rA parameter. + * + * @param rA + * the subtrahend relation + */ + public void difference(DRedTcRelation rA) { + for (V source : rA.tuplesForward.keySet()) { + for (V target : rA.tuplesForward.get(source)) { + this.removeTuple(source, target); + } + } + } + + @Override + public Set getTupleEnds(V source) { + Set t = tuplesForward.get(source); + return (t == null) ? new HashSet() : new HashSet(t); + } + + /** + * Returns the set of nodes from which the target node is reachable. + * + * @param target + * the target node + * @return the set of source nodes + */ + public Set getTupleStarts(V target) { + Set t = tuplesBackward.get(target); + return (t == null) ? new HashSet() : new HashSet(t); + } + + @Override + public Set getTupleStarts() { + Set t = tuplesForward.keySet(); + return new HashSet(t); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("TcRelation = "); + + for (Entry> entry : this.tuplesForward.entrySet()) { + V source = entry.getKey(); + for (V target : entry.getValue()) { + sb.append("(" + source + "," + target + ") "); + } + } + return sb.toString(); + } + + /** + * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise. + * + * @param source + * the source node + * @param target + * the target node + * @return true if tuple is present, false otherwise + */ + public boolean containsTuple(V source, V target) { + if (tuplesForward.containsKey(source)) { + if (tuplesForward.get(source).contains(target)) + return true; + } + return false; + } + + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if ((obj == null) || (obj.getClass() != this.getClass())) { + return false; + } + + DRedTcRelation aTR = (DRedTcRelation) obj; + + for (Entry> entry : aTR.tuplesForward.entrySet()) { + V source = entry.getKey(); + for (V target : entry.getValue()) { + if (!this.containsTuple(source, target)) + return false; + } + } + + for (Entry> entry : this.tuplesForward.entrySet()) { + V source = entry.getKey(); + for (V target : entry.getValue()) { + if (!aTR.containsTuple(source, target)) + return false; + } + } + + return true; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 31 * hash + tuplesForward.hashCode(); + hash = 31 * hash + tuplesBackward.hashCode(); + return hash; + } + + public Map> getTuplesForward() { + return tuplesForward; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java new file mode 100644 index 00000000..8b8908b1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.fw; + +import java.util.HashMap; +import java.util.Map; + +import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; + +public class FloydWarshallAlg implements IGraphObserver { + + private DRedTcRelation tc = null; + private IBiDirectionalGraphDataSource gds = null; + + public FloydWarshallAlg(IGraphDataSource gds) { + if (gds instanceof IBiDirectionalGraphDataSource) { + this.gds = (IBiDirectionalGraphDataSource) gds; + } else { + this.gds = new IBiDirectionalWrapper(gds); + } + + this.tc = new DRedTcRelation(); + gds.attachObserver(this); + generateTc(); + } + + private void generateTc() { + + tc.clear(); + + int n = gds.getAllNodes().size(); + Map mapForw = new HashMap(); + Map mapBackw = new HashMap(); + int[][] P = new int[n][n]; + + int i, j, k; + + // initialize adjacent matrix + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + P[i][j] = 0; + } + } + + i = 0; + for (V node : gds.getAllNodes()) { + mapForw.put(node, i); + mapBackw.put(i, node); + i++; + } + + for (V source : gds.getAllNodes()) { + IMemoryView targets = gds.getTargetNodes(source); + for (V target : targets.distinctValues()) { + P[mapForw.get(source)][mapForw.get(target)] = 1; + } + } + + for (k = 0; k < n; k++) { + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + P[i][j] = P[i][j] | (P[i][k] & P[k][j]); + } + } + } + + for (i = 0; i < n; i++) { + for (j = 0; j < n; j++) { + if (P[i][j] == 1 && i != j) + tc.addTuple(mapBackw.get(i), mapBackw.get(j)); + } + } + } + + @Override + public void edgeInserted(V source, V target) { + generateTc(); + } + + @Override + public void edgeDeleted(V source, V target) { + generateTc(); + } + + @Override + public void nodeInserted(V n) { + generateTc(); + } + + @Override + public void nodeDeleted(V n) { + generateTc(); + } + + public DRedTcRelation getTcRelation() { + return this.tc; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java new file mode 100644 index 00000000..fdf64f77 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.base.itc.alg.incscc; + +import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; +import tools.refinery.viatra.runtime.matchers.util.Direction; + +/** + * @author Tamas Szabo + * + */ +public class CountingListener implements ITcObserver { + + private IncSCCAlg alg; + + public CountingListener(IncSCCAlg alg) { + this.alg = alg; + } + + @Override + public void tupleInserted(V source, V target) { + alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.INSERT); + } + + @Override + public void tupleDeleted(V source, V target) { + alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.DELETE); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java new file mode 100644 index 00000000..5d720e75 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java @@ -0,0 +1,645 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.incscc; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.alg.counting.CountingAlg; +import tools.refinery.viatra.runtime.base.itc.alg.misc.bfs.BFS; +import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; +import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCCResult; +import tools.refinery.viatra.runtime.base.itc.alg.util.CollectionHelper; +import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; +import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; +import tools.refinery.viatra.runtime.base.itc.alg.misc.GraphHelper; +import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; +import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple; +import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; +import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; +import tools.refinery.viatra.runtime.matchers.algorithms.UnionFind; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; + +/** + * Incremental SCC maintenance + counting algorithm. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the nodes in the graph data source + */ +public class IncSCCAlg implements IGraphObserver, ITcDataSource { + + public UnionFind sccs; + public IBiDirectionalGraphDataSource gds; + private CountingAlg counting; + private Graph reducedGraph; + private IBiDirectionalGraphDataSource reducedGraphIndexer; + private List> observers; + private CountingListener countingListener; + + public IncSCCAlg(IGraphDataSource graphDataSource) { + + if (graphDataSource instanceof IBiDirectionalGraphDataSource) { + gds = (IBiDirectionalGraphDataSource) graphDataSource; + } else { + gds = new IBiDirectionalWrapper(graphDataSource); + } + observers = CollectionsFactory.createObserverList(); + sccs = new UnionFind(); + reducedGraph = new Graph(); + reducedGraphIndexer = new IBiDirectionalWrapper(reducedGraph); + countingListener = new CountingListener(this); + initalizeInternalDataStructures(); + gds.attachObserver(this); + } + + private void initalizeInternalDataStructures() { + SCCResult _sccres = SCC.computeSCC(gds); + Set> _sccs = _sccres.getSccs(); + + for (Set _set : _sccs) { + sccs.makeSet(_set); + } + + // Initalization of the reduced graph + for (V n : sccs.getPartitionHeads()) { + reducedGraph.insertNode(n); + } + + for (V source : gds.getAllNodes()) { + final IMemoryView targetNodes = gds.getTargetNodes(source); + for (Entry entry : targetNodes.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V target = entry.getKey(); + V sourceRoot = sccs.find(source); + V targetRoot = sccs.find(target); + + if (!sourceRoot.equals(targetRoot)) { + reducedGraph.insertEdge(sourceRoot, targetRoot); + } + } + } + } + + counting = new CountingAlg(reducedGraph); + } + + @Override + public void edgeInserted(V source, V target) { + V sourceRoot = sccs.find(source); + V targetRoot = sccs.find(target); + + // Different SCC + if (!sourceRoot.equals(targetRoot)) { + + // source is reachable from target? + if (counting.isReachable(targetRoot, sourceRoot)) { + + Set predecessorRoots = counting.getAllReachableSources(sourceRoot); + Set successorRoots = counting.getAllReachableTargets(targetRoot); + + // 1. intersection of source and target roots, these will be in the merged SCC + Set isectRoots = CollectionHelper.intersection(predecessorRoots, successorRoots); + isectRoots.add(sourceRoot); + isectRoots.add(targetRoot); + + // notifications must be issued before Union-Find modifications + if (observers.size() > 0) { + Set sourceSCCs = createSetNullTolerant(predecessorRoots); + sourceSCCs.add(sourceRoot); + Set targetSCCs = createSetNullTolerant(successorRoots); + targetSCCs.add(targetRoot); + + // tracing back to actual nodes + for (V sourceSCC : sourceSCCs) { + targetLoop: for (V targetSCC : targetSCCs) { + if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; + + boolean needsNotification = + // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. + // Issue notifications only if there is no self-loop present at the moment + (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper + .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) + || + // Case 2. sourceSCC and targetSCC are different sccs. + (!sourceSCC.equals(targetSCC)); + // if self loop is already present omit the notification + if (needsNotification) { + notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), + Direction.INSERT); + } + } + } + } + + // 2. delete edges, nodes + List sourceSCCs = new ArrayList(); + List targetSCCs = new ArrayList(); + + for (V r : isectRoots) { + List sourceSCCsOfSCC = getSourceSCCsOfSCC(r); + List targetSCCsOfSCC = getTargetSCCsOfSCC(r); + + for (V sourceSCC : sourceSCCsOfSCC) { + if (!sourceSCC.equals(r)) { + reducedGraph.deleteEdgeIfExists(sourceSCC, r); + } + } + + for (V targetSCC : targetSCCsOfSCC) { + if (!isectRoots.contains(targetSCC) && !r.equals(targetSCC)) { + reducedGraph.deleteEdgeIfExists(r, targetSCC); + } + } + + sourceSCCs.addAll(sourceSCCsOfSCC); + targetSCCs.addAll(targetSCCsOfSCC); + } + + for (V r : isectRoots) { + reducedGraph.deleteNode(r); + } + + // 3. union + Iterator iterator = isectRoots.iterator(); + V newRoot = iterator.next(); + while (iterator.hasNext()) { + newRoot = sccs.union(newRoot, iterator.next()); + } + + // 4. add new node + reducedGraph.insertNode(newRoot); + + // 5. add edges + Set containedNodes = sccs.getPartition(newRoot); + + for (V sourceSCC : sourceSCCs) { + if (!containedNodes.contains(sourceSCC) && !sourceSCC.equals(newRoot)) { + reducedGraph.insertEdge(sourceSCC, newRoot); + } + } + for (V targetSCC : targetSCCs) { + if (!containedNodes.contains(targetSCC) && !targetSCC.equals(newRoot)) { + reducedGraph.insertEdge(newRoot, targetSCC); + } + } + } else { + if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 1) { + counting.attachObserver(countingListener); + } + reducedGraph.insertEdge(sourceRoot, targetRoot); + counting.detachObserver(countingListener); + } + } else { + // Notifications about self-loops + if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 + && GraphHelper.getEdgeCount(source, target, gds) == 1) { + notifyTcObservers(source, source, Direction.INSERT); + } + } + } + + @Override + public void edgeDeleted(V source, V target) { + V sourceRoot = sccs.find(source); + V targetRoot = sccs.find(target); + + if (!sourceRoot.equals(targetRoot)) { + if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 0) { + counting.attachObserver(countingListener); + } + reducedGraph.deleteEdgeIfExists(sourceRoot, targetRoot); + counting.detachObserver(countingListener); + } else { + // get the graph for the scc whose root is sourceRoot + Graph g = GraphHelper.getSubGraph(sccs.getPartition(sourceRoot), gds); + + // if source is not reachable from target anymore + if (!BFS.isReachable(source, target, g)) { + // create copies of the current state before destructive manipulation + Map reachableSources = CollectionsFactory.createMap(); + for (Entry entry : reducedGraphIndexer.getSourceNodes(sourceRoot).entriesWithMultiplicities()) { + reachableSources.put(entry.getKey(), entry.getValue()); + } + Map reachableTargets = CollectionsFactory.createMap(); + for (Entry entry : reducedGraphIndexer.getTargetNodes(sourceRoot).entriesWithMultiplicities()) { + reachableTargets.put(entry.getKey(), entry.getValue()); + } + + SCCResult _newSccs = SCC.computeSCC(g); + + // delete scc node (and with its edges too) + for (Entry entry : reachableSources.entrySet()) { + V s = entry.getKey(); + for (int i = 0; i < entry.getValue(); i++) { + reducedGraph.deleteEdgeIfExists(s, sourceRoot); + } + } + + for (Entry entry : reachableTargets.entrySet()) { + V t = entry.getKey(); + for (int i = 0; i < entry.getValue(); i++) { + reducedGraph.deleteEdgeIfExists(sourceRoot, t); + } + } + + sccs.deleteSet(sourceRoot); + reducedGraph.deleteNode(sourceRoot); + + Set> newSCCs = _newSccs.getSccs(); + Set newSCCRoots = CollectionsFactory.createSet(); + + // add new nodes and edges to the reduced graph + for (Set newSCC : newSCCs) { + V newRoot = sccs.makeSet(newSCC); + reducedGraph.insertNode(newRoot); + newSCCRoots.add(newRoot); + } + for (V newSCCRoot : newSCCRoots) { + List sourceSCCsOfSCC = getSourceSCCsOfSCC(newSCCRoot); + List targetSCCsOfSCC = getTargetSCCsOfSCC(newSCCRoot); + + for (V sourceSCC : sourceSCCsOfSCC) { + if (!sourceSCC.equals(newSCCRoot)) { + reducedGraph.insertEdge(sccs.find(sourceSCC), newSCCRoot); + } + } + for (V targetSCC : targetSCCsOfSCC) { + if (!newSCCRoots.contains(targetSCC) && !targetSCC.equals(newSCCRoot)) + reducedGraph.insertEdge(newSCCRoot, targetSCC); + } + } + + // Must be after the union-find modifications + if (observers.size() > 0) { + V newSourceRoot = sccs.find(source); + V newTargetRoot = sccs.find(target); + + Set sourceSCCs = createSetNullTolerant(counting.getAllReachableSources(newSourceRoot)); + sourceSCCs.add(newSourceRoot); + + Set targetSCCs = createSetNullTolerant(counting.getAllReachableTargets(newTargetRoot)); + targetSCCs.add(newTargetRoot); + + for (V sourceSCC : sourceSCCs) { + targetLoop: for (V targetSCC : targetSCCs) { + if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; + + boolean needsNotification = + // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. + // Issue notifications only if there is no self-loop present at the moment + (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper + .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) + || + // Case 2. sourceSCC and targetSCC are different sccs. + (!sourceSCC.equals(targetSCC)); + // if self loop is already present omit the notification + if (needsNotification) { + notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), + Direction.DELETE); + } + } + } + } + } else { + // only handle self-loop notifications - sourceRoot equals to targetRoot + if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 + && GraphHelper.getEdgeCount(source, target, gds) == 0) { + notifyTcObservers(source, source, Direction.DELETE); + } + } + } + } + + @Override + public void nodeInserted(V n) { + sccs.makeSet(n); + reducedGraph.insertNode(n); + } + + @Override + public void nodeDeleted(V n) { + IMemoryView sources = gds.getSourceNodes(n); + IMemoryView targets = gds.getTargetNodes(n); + + for (Entry entry : sources.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V source = entry.getKey(); + edgeDeleted(source, n); + } + } + + for (Entry entry : targets.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V target = entry.getKey(); + edgeDeleted(n, target); + } + } + + sccs.deleteSet(n); + } + + @Override + public void attachObserver(ITcObserver to) { + observers.add(to); + } + + @Override + public void detachObserver(ITcObserver to) { + observers.remove(to); + } + + @Override + public Set getAllReachableTargets(V source) { + V sourceRoot = sccs.find(source); + Set containedNodes = sccs.getPartition(sourceRoot); + Set targets = CollectionsFactory.createSet(); + + if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(source, gds) == 1) { + targets.addAll(containedNodes); + } + + Set rootSet = counting.getAllReachableTargets(sourceRoot); + if (rootSet != null) { + for (V _root : rootSet) { + targets.addAll(sccs.getPartition(_root)); + } + } + + return targets; + } + + @Override + public Set getAllReachableSources(V target) { + V targetRoot = sccs.find(target); + Set containedNodes = sccs.getPartition(targetRoot); + Set sources = CollectionsFactory.createSet(); + + if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(target, gds) == 1) { + sources.addAll(containedNodes); + } + + Set rootSet = counting.getAllReachableSources(targetRoot); + if (rootSet != null) { + for (V _root : rootSet) { + sources.addAll(sccs.getPartition(_root)); + } + } + return sources; + } + + @Override + public boolean isReachable(V source, V target) { + V sourceRoot = sccs.find(source); + V targetRoot = sccs.find(target); + + if (sourceRoot.equals(targetRoot)) + return true; + else + return counting.isReachable(sourceRoot, targetRoot); + } + + public List getReachabilityPath(V source, V target) { + if (!isReachable(source, target)) { + return null; + } else { + Set sccsInSubGraph = CollectionHelper.intersection(counting.getAllReachableTargets(source), + counting.getAllReachableSources(target)); + sccsInSubGraph.add(sccs.find(source)); + sccsInSubGraph.add(sccs.find(target)); + Set nodesInSubGraph = CollectionsFactory.createSet(); + + for (V sccRoot : sccsInSubGraph) { + nodesInSubGraph.addAll(sccs.getPartition(sccRoot)); + } + + return GraphHelper.constructPath(source, target, nodesInSubGraph, gds); + } + } + + // for JUnit + public boolean checkTcRelation(DRedTcRelation tc) { + + for (V s : tc.getTupleStarts()) { + for (V t : tc.getTupleEnds(s)) { + if (!isReachable(s, t)) + return false; + } + } + + for (V root : counting.getTcRelation().getTupleStarts()) { + for (V end : counting.getTcRelation().getTupleEnds(root)) { + for (V s : sccs.getPartition(root)) { + for (V t : sccs.getPartition(end)) { + if (!tc.containsTuple(s, t)) + return false; + } + } + } + } + + return true; + } + + /** + * Return the SCCs from which the SCC represented by the root node is reachable. Note that an SCC can be present + * multiple times in the returned list (multiple edges between the two SCCs). + * + * @param root + * @return the list of reachable target SCCs + */ + private List getSourceSCCsOfSCC(V root) { + List sourceSCCs = new ArrayList(); + + for (V containedNode : this.sccs.getPartition(root)) { + IMemoryView sourceNodes = this.gds.getSourceNodes(containedNode); + for (V source : sourceNodes.distinctValues()) { + sourceSCCs.add(this.sccs.find(source)); + } + } + + return sourceSCCs; + } + + /** + * Returns true if the SCC represented by the given root node has incoming edges in the reduced graph, + * false otherwise (if this SCC is a source in the reduced graph). + * + * @param root the root node of an SCC + * @return true if it has incoming edges, false otherwise + * @since 1.6 + */ + public boolean hasIncomingEdges(final V root) { + for (final V containedNode : this.sccs.getPartition(root)) { + final IMemoryView sourceNodes = this.gds.getSourceNodes(containedNode); + for (final V source : sourceNodes.distinctValues()) { + final V otherRoot = this.sccs.find(source); + if (!Objects.equals(root, otherRoot)) { + return true; + } + } + } + return false; + } + + /** + * Return the SCCs which are reachable from the SCC represented by the root node. Note that an SCC can be present + * multiple times in the returned list (multiple edges between the two SCCs). + * + * @param root + * @return the list of reachable target SCCs + */ + private List getTargetSCCsOfSCC(V root) { + List targetSCCs = new ArrayList(); + + for (V containedNode : this.sccs.getPartition(root)) { + IMemoryView targetNodes = this.gds.getTargetNodes(containedNode); + for (V target : targetNodes.distinctValues()) { + targetSCCs.add(this.sccs.find(target)); + } + } + + return targetSCCs; + } + + /** + * Returns true if the SCC represented by the given root node has outgoing edges in the reduced graph, + * false otherwise (if this SCC is a sink in the reduced graph). + * + * @param root the root node of an SCC + * @return true if it has outgoing edges, false otherwise + * @since 1.6 + */ + public boolean hasOutgoingEdges(V root) { + for (final V containedNode : this.sccs.getPartition(root)) { + final IMemoryView targetNodes = this.gds.getTargetNodes(containedNode); + for (final V target : targetNodes.distinctValues()) { + final V otherRoot = this.sccs.find(target); + if (!Objects.equals(root, otherRoot)) { + return true; + } + } + } + return false; + } + + @Override + public void dispose() { + gds.detachObserver(this); + counting.dispose(); + } + + /** + * Call this method to notify the observers of the transitive closure relation. The tuples used in the notification + * will be the Descartes product of the two sets given. + * + * @param sources + * the source nodes + * @param targets + * the target nodes + * @param direction + */ + protected void notifyTcObservers(Set sources, Set targets, Direction direction) { + for (V s : sources) { + for (V t : targets) { + notifyTcObservers(s, t, direction); + } + } + } + + private void notifyTcObservers(V source, V target, Direction direction) { + for (ITcObserver observer : observers) { + if (direction == Direction.INSERT) { + observer.tupleInserted(source, target); + } + if (direction == Direction.DELETE) { + observer.tupleDeleted(source, target); + } + } + } + + /** + * Returns the node that is selected as the representative of the SCC containing the argument. + * @since 1.6 + */ + public V getRepresentative(V node) { + return sccs.find(node); + } + + public Set> getTcRelation() { + Set> resultSet = new HashSet>(); + + for (V sourceRoot : sccs.getPartitionHeads()) { + Set sources = sccs.getPartition(sourceRoot); + if (sources.size() > 1 || GraphHelper.getEdgeCount(sources.iterator().next(), gds) == 1) { + for (V source : sources) { + for (V target : sources) { + resultSet.add(new Tuple(source, target)); + } + } + } + + Set reachableTargets = counting.getAllReachableTargets(sourceRoot); + if (reachableTargets != null) { + for (V targetRoot : reachableTargets) { + for (V source : sources) { + for (V target : sccs.getPartition(targetRoot)) { + resultSet.add(new Tuple(source, target)); + } + } + } + } + } + + return resultSet; + } + + public boolean isIsolated(V node) { + IMemoryView targets = gds.getTargetNodes(node); + IMemoryView sources = gds.getSourceNodes(node); + return targets.isEmpty() && sources.isEmpty(); + } + + @Override + public IGraphPathFinder getPathFinder() { + return new DFSPathFinder(gds, this); + } + + /** + * The graph of SCCs; each SCC is represented by its representative node (see {@link #getRepresentative(Object)}) + * @since 1.6 + */ + public Graph getReducedGraph() { + return reducedGraph; + } + + private static Set createSetNullTolerant(Set initial) { + if (initial != null) + return CollectionsFactory.createSet(initial); + else + return CollectionsFactory.createSet(); + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java new file mode 100644 index 00000000..51017b1a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Abel Hegedus and IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.base.itc.alg.misc; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; + +/** + * A depth-first search implementation of the {@link IGraphPathFinder}. + * + * TODO use ITC to filter nodes that must be traversed, instead of checks + * + * @author Abel Hegedus + * + * @param + * the node type of the graph + */ +public class DFSPathFinder implements IGraphPathFinder { + + private IGraphDataSource graph; + private ITcDataSource itc; + + public DFSPathFinder(IGraphDataSource graph, ITcDataSource itc) { + this.graph = graph; + this.itc = itc; + } + + @Override + public Iterable> getAllPaths(V sourceNode, V targetNode) { + Set endNodes = new HashSet(); + endNodes.add(targetNode); + return getAllPathsToTargets(sourceNode, endNodes); + } + + @Override + public Iterable> getAllPathsToTargets(V sourceNode, Set targetNodes) { + List> paths = new ArrayList>(); + Deque visited = new LinkedList(); + Set reachableTargets = new HashSet(); + for (V targetNode : targetNodes) { + if (itc.isReachable(sourceNode, targetNode)) { + reachableTargets.add(targetNode); + } + } + if (!reachableTargets.isEmpty()) { + return paths; + } + visited.add(sourceNode); + return getPaths(paths, visited, reachableTargets); + } + + protected Iterable> getPaths(List> paths, Deque visited, Set targetNodes) { + IMemoryView nodes = graph.getTargetNodes(visited.getLast()); + // examine adjacent nodes + for (V node : nodes.distinctValues()) { + if (visited.contains(node)) { + continue; + } + if (targetNodes.contains(node)) { + visited.add(node); + // clone visited LinkedList + Deque visitedClone = new LinkedList(visited); + paths.add(visitedClone); + visited.removeLast(); + break; + } + } + + // in breadth-first, recursion needs to come after visiting connected nodes + for (V node : nodes.distinctValues()) { + if (visited.contains(node) || targetNodes.contains(node)) { + continue; + } + boolean canReachTarget = false; + for (V target : targetNodes) { + if (itc.isReachable(node, target)) { + canReachTarget = true; + break; + } + } + if (canReachTarget) { + visited.addLast(node); + getPaths(paths, visited, targetNodes); + visited.removeLast(); + } + } + + return paths; + } + + public String printPaths(List> paths) { + StringBuilder sb = new StringBuilder(); + for (Deque visited : paths) { + sb.append("Path: "); + for (V node : visited) { + sb.append(node); + sb.append(" --> "); + } + sb.append("\n"); + } + return sb.toString(); + } + + @Override + public Deque getPath(V sourceNode, V targetNode) { + // TODO optimize + Iterable> allPaths = getAllPaths(sourceNode, targetNode); + Iterator> pathIterator = allPaths.iterator(); + return pathIterator.hasNext() ? pathIterator.next() : new LinkedList(); + } + + @Override + public Iterable> getShortestPaths(V sourceNode, V targetNode) { + // TODO optimize + Iterable> allPaths = getAllPaths(sourceNode, targetNode); + List> shortestPaths = new ArrayList>(); + int shortestPathLength = -1; + for (Deque path : allPaths) { + int pathLength = path.size(); + if (shortestPathLength == -1 || pathLength < shortestPathLength) { + shortestPaths.clear(); + shortestPathLength = pathLength; + } + if (pathLength == shortestPathLength) { + shortestPaths.add(path); + } + } + return shortestPaths; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java new file mode 100644 index 00000000..cf68d36a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.misc; + +public class Edge { + private V source; + private V target; + + public Edge(V source, V target) { + super(); + this.source = source; + this.target = target; + } + + public V getSource() { + return source; + } + + public void setSource(V source) { + this.source = source; + } + + public V getTarget() { + return target; + } + + public void setTarget(V target) { + this.target = target; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java new file mode 100644 index 00000000..194e979b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.base.itc.alg.misc; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; + +/** + * Utility class for graph related operations. + * + * @author Tamas Szabo + */ +public class GraphHelper { + + private GraphHelper() {/*Utility class constructor*/} + + /** + * Returns the subgraph from the given {@link IBiDirectionalGraphDataSource} which contains the given set of nodes. + * + * @param nodesInSubGraph + * the nodes that are present in the subgraph + * @param graphDataSource + * the graph data source for the original graph + * @return the subgraph associated to the given nodes + */ + public static Graph getSubGraph(Collection nodesInSubGraph, + IBiDirectionalGraphDataSource graphDataSource) { + Graph g = new Graph(); + if (nodesInSubGraph != null) { + for (V node : nodesInSubGraph) { + g.insertNode(node); + } + + for (V node : nodesInSubGraph) { + IMemoryView sources = graphDataSource.getSourceNodes(node); + for (Entry entry : sources.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V s = entry.getKey(); + if (nodesInSubGraph.contains(s)) { + g.insertEdge(s, node); + } + } + } + } + } + + return g; + } + + /** + * Constructs a path between source and target in the given graph. Both the {@link IGraphDataSource} and the set of + * nodes are used, this way it is possible to construct a path in a given subgraph. + * + * The returned {@link List} contains the nodes along the path (this means that there is an edge in the graph + * between two consecutive nodes). A self loop (one edge) is indicated with the source node being present two times + * in the returned {@link List}. + * + * @param source + * the source node + * @param target + * the target node + * @param nodesInGraph + * the nodes that are present in the subgraph + * @param graphDataSource + * the graph data source + * @return the path between the two nodes + */ + public static List constructPath(V source, V target, Set nodesInGraph, + IGraphDataSource graphDataSource) { + Set visitedNodes = new HashSet(); + List path = new ArrayList(); + + visitedNodes.add(source); + path.add(source); + V act = source; + + // if source and target are the same node + if (source.equals(target) && graphDataSource.getTargetNodes(source).containsNonZero(target)) { + // the node will be present in the path two times + path.add(source); + return path; + } else { + while (act != null) { + V nextNode = getNextNodeToVisit(act, graphDataSource, nodesInGraph, visitedNodes); + if (nextNode == null && path.size() > 1) { + // needs to backtrack along path + // remove the last element in the path because we can't go + // anywhere from there + path.remove(path.size() - 1); + while (nextNode == null && path.size() > 0) { + V lastPathElement = path.get(path.size() - 1); + nextNode = getNextNodeToVisit(lastPathElement, graphDataSource, nodesInGraph, visitedNodes); + if (nextNode == null) { + path.remove(path.size() - 1); + } + } + } + + if (nextNode != null) { + visitedNodes.add(nextNode); + path.add(nextNode); + if (nextNode.equals(target)) { + return path; + } + } + act = nextNode; + } + return null; + } + } + + private static V getNextNodeToVisit(V act, IGraphDataSource graphDataSource, Set nodesInSubGraph, + Set visitedNodes) { + IMemoryView targetNodes = graphDataSource.getTargetNodes(act); + for (Entry entry : targetNodes.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V node = entry.getKey(); + if (nodesInSubGraph.contains(node) && !visitedNodes.contains(node)) { + return node; + } + } + } + return null; + } + + /** + * Returns the number of self-loop edges for the given node. + * + * @param node + * the node + * @param graphDataSource + * the graph data source + * @return the number of self-loop edges + */ + public static int getEdgeCount(V node, IGraphDataSource graphDataSource) { + return getEdgeCount(node, node, graphDataSource); + } + + /** + * Returns the number of edges between the given source and target nodes. + * + * @param source + * the source node + * @param target + * the target node + * @param graphDataSource + * the graph data source + * @return the number of parallel edges between the two nodes + */ + public static int getEdgeCount(V source, V target, IGraphDataSource graphDataSource) { + Integer count = graphDataSource.getTargetNodes(source).getCount(target); + if (count == null) { + return 0; + } else { + return count; + } + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java new file mode 100644 index 00000000..cebb09c8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.base.itc.alg.misc; + +import java.util.Deque; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; + +/** + * The path finder provides methods for retrieving paths in a graph between a source node and one or more target nodes. + * Use {@link ITcDataSource#getPathFinder()} for instantiating. + * + * @author Abel Hegedus + * + * @param the node type of the graph + */ +public interface IGraphPathFinder { + + /** + * Returns an arbitrary path from the source node to the target node (if such exists). If there is no path + * between them, an empty collection is returned. + * + * @param sourceNode the source node of the path + * @param targetNode the target node of the path + * @return the path from the source to the target, or empty collection if target is not reachable from source. + */ + Deque getPath(V sourceNode, V targetNode); + + /** + * Returns the collection of shortest paths from the source node to the target node (if such exists). If there is no path + * between them, an empty collection is returned. + * + * @param sourceNode the source node of the path + * @param targetNode the target node of the path + * @return the collection of shortest paths from the source to the target, or empty collection if target is not reachable from source. + */ + Iterable> getShortestPaths(V sourceNode, V targetNode); + + /** + * Returns the collection of paths from the source node to the target node (if such exists). If there is no path + * between them, an empty collection is returned. + * + * @param sourceNode the source node of the path + * @param targetNode the target node of the path + * @return the collection of paths from the source to the target, or empty collection if target is not reachable from source. + */ + Iterable> getAllPaths(V sourceNode, V targetNode); + + /** + * Returns the collection of paths from the source node to any of the target nodes (if such exists). If there is no path + * between them, an empty collection is returned. + * + * @param sourceNode the source node of the path + * @param targetNodes the set of target nodes of the paths + * @return the collection of paths from the source to any of the targets, or empty collection if neither target is reachable from source. + */ + Iterable> getAllPathsToTargets(V sourceNode, Set targetNodes); + + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java new file mode 100644 index 00000000..a41ff6c7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.misc; + +import java.util.Set; + +public interface ITcRelation { + + /** + * Returns the starting nodes from a transitive closure relation. + * + * @return the set of starting nodes + */ + public Set getTupleStarts(); + + /** + * Returns the set of nodes that are reachable from the given node. + * + * @param start + * the starting node + * @return the set of reachable nodes + */ + public Set getTupleEnds(V start); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java new file mode 100644 index 00000000..a2d54a59 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.misc; + +public class Tuple { + + private V source; + private V target; + + public Tuple(V source, V target) { + super(); + this.source = source; + this.target = target; + } + + public V getSource() { + return source; + } + + public void setSource(V source) { + this.source = source; + } + + public V getTarget() { + return target; + } + + public void setTarget(V target) { + this.target = target; + } + + @Override + public String toString() { + return "(" + source.toString() + "," + target.toString() + ")"; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Tuple) { + Tuple t = (Tuple) o; + + if (t.getSource().equals(this.source) && t.getTarget().equals(this.target)) { + return true; + } + } + return false; + } + + @Override + public int hashCode() { + return source.hashCode() + target.hashCode(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java new file mode 100644 index 00000000..798f31d2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.misc.bfs; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; + +public class BFS { + + private BFS() {/*Utility class constructor*/} + + /** + * Performs a breadth first search on the given graph to determine whether source is reachable from target. + * + * @param + * the type parameter of the nodes in the graph + * @param source + * the source node + * @param target + * the target node + * @param graph + * the graph data source + * @return true if source is reachable from target, false otherwise + */ + public static boolean isReachable(V source, V target, IGraphDataSource graph) { + List nodeQueue = new ArrayList(); + Set visited = new HashSet(); + + nodeQueue.add(source); + visited.add(source); + + boolean ret = _isReachable(target, graph, nodeQueue, visited); + return ret; + } + + private static boolean _isReachable(V target, IGraphDataSource graph, List nodeQueue, Set visited) { + + while (!nodeQueue.isEmpty()) { + V node = nodeQueue.remove(0); + for (V t : graph.getTargetNodes(node).distinctValues()){ + if (t.equals(target)) { + return true; + } + if (!visited.contains(t)) { + visited.add(t); + nodeQueue.add(t); + } + } + } + return false; + } + + public static Set reachableSources(IBiDirectionalGraphDataSource graph, V target) { + Set retSet = new HashSet(); + retSet.add(target); + List nodeQueue = new ArrayList(); + nodeQueue.add(target); + + _reachableSources(graph, nodeQueue, retSet); + + return retSet; + } + + private static void _reachableSources(IBiDirectionalGraphDataSource graph, List nodeQueue, + Set retSet) { + while (!nodeQueue.isEmpty()) { + V node = nodeQueue.remove(0); + for (V _node : graph.getSourceNodes(node).distinctValues()) { + if (!retSet.contains(_node)) { + retSet.add(_node); + nodeQueue.add(_node); + } + } + } + } + + public static Set reachableTargets(IGraphDataSource graph, V source) { + Set retSet = new HashSet(); + retSet.add(source); + List nodeQueue = new ArrayList(); + nodeQueue.add(source); + + _reachableTargets(graph, nodeQueue, retSet); + + return retSet; + } + + private static void _reachableTargets(IGraphDataSource graph, List nodeQueue, Set retSet) { + while (!nodeQueue.isEmpty()) { + V node = nodeQueue.remove(0); + + for (V _node : graph.getTargetNodes(node).distinctValues()) { + + if (!retSet.contains(_node)) { + retSet.add(_node); + nodeQueue.add(_node); + } + } + } + } + + /** + * Performs a breadth first search on the given graph and collects all the nodes along the path from source to + * target if such path exists. + * + * @param + * the type parameter of the nodes in the graph + * @param source + * the source node + * @param target + * the target node + * @param graph + * the graph data source + * @return the set of nodes along the path + */ + public static Set collectNodesAlongPath(V source, V target, IGraphDataSource graph) { + Set path = new HashSet(); + _collectNodesAlongPath(source, target, graph, path); + return path; + } + + private static boolean _collectNodesAlongPath(V node, V target, IGraphDataSource graph, Set path) { + + boolean res = false; + + // end recursion + if (node.equals(target)) { + path.add(node); + return true; + } else { + for (V _nodeT : graph.getTargetNodes(node).distinctValues()) { + res = (_collectNodesAlongPath(_nodeT, target, graph, path)) || res; + } + if (res) + path.add(node); + return res; + } + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java new file mode 100644 index 00000000..c8d25c4e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.misc.dfs; + +import java.util.HashMap; + +import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; + +public class DFSAlg implements IGraphObserver { + + private IGraphDataSource gds; + private DRedTcRelation tc; + private int[] visited; + private HashMap nodeMap; + + public DFSAlg(IGraphDataSource gds) { + this.gds = gds; + this.tc = new DRedTcRelation(); + gds.attachObserver(this); + deriveTc(); + } + + private void deriveTc() { + tc.clear(); + + this.visited = new int[gds.getAllNodes().size()]; + nodeMap = new HashMap(); + + int j = 0; + for (V n : gds.getAllNodes()) { + nodeMap.put(n, j); + j++; + } + + for (V n : gds.getAllNodes()) { + oneDFS(n, n); + initVisitedArray(); + } + } + + private void initVisitedArray() { + for (int i = 0; i < visited.length; i++) + visited[i] = 0; + } + + private void oneDFS(V act, V source) { + + if (!act.equals(source)) { + tc.addTuple(source, act); + } + + visited[nodeMap.get(act)] = 1; + + for (V t : gds.getTargetNodes(act).distinctValues()) { + if (visited[nodeMap.get(t)] == 0) { + oneDFS(t, source); + } + } + } + + public DRedTcRelation getTcRelation() { + return this.tc; + } + + @Override + public void edgeInserted(V source, V target) { + deriveTc(); + } + + @Override + public void edgeDeleted(V source, V target) { + deriveTc(); + } + + @Override + public void nodeInserted(V n) { + deriveTc(); + } + + @Override + public void nodeDeleted(V n) { + deriveTc(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java new file mode 100644 index 00000000..c99a48ab --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; + +public class PKAlg implements IGraphObserver { + + /** + * Maps the nodes to their indicies. + */ + private Map node2index; + private Map index2node; + private Map node2mark; + + /** + * Maps the index of a node to the index in the topsort. + */ + private Map index2topsort; + private Map topsort2index; + + /** + * Index associated to the inserted nodes (incrementing with every insertion). + */ + private int index; + + /** + * Index within the topsort for the target node when edge insertion occurs. + */ + private int lower_bound; + + /** + * Index within the topsort for the source node when edge insertion occurs. + */ + private int upper_bound; + + private List RF; + private List RB; + private IBiDirectionalGraphDataSource gds; + + public PKAlg(IGraphDataSource gds) { + if (gds instanceof IBiDirectionalGraphDataSource) { + this.gds = (IBiDirectionalGraphDataSource) gds; + } else { + this.gds = new IBiDirectionalWrapper(gds); + } + + node2mark = new HashMap(); + node2index = new HashMap(); + index2node = new HashMap(); + index2topsort = new HashMap(); + topsort2index = new HashMap(); + index = 0; + + gds.attachObserver(this); + } + + @Override + public void edgeInserted(V source, V target) { + + RF = new ArrayList(); + RB = new ArrayList(); + + lower_bound = index2topsort.get(node2index.get(target)); + upper_bound = index2topsort.get(node2index.get(source)); + + if (lower_bound < upper_bound) { + dfsForward(target); + dfsBackward(source); + reorder(); + } + } + + private List getIndicies(List list) { + List indicies = new ArrayList(); + + for (V n : list) + indicies.add(index2topsort.get(node2index.get(n))); + + return indicies; + } + + private void reorder() { + + Collections.reverse(RB); + + // azon csomopontok indexei amelyek sorrendje nem jo + List L = getIndicies(RF); + L.addAll(getIndicies(RB)); + Collections.sort(L); + + for (int i = 0; i < RB.size(); i++) { + index2topsort.put(node2index.get(RB.get(i)), L.get(i)); + topsort2index.put(L.get(i), node2index.get(RB.get(i))); + } + + for (int i = 0; i < RF.size(); i++) { + index2topsort.put(node2index.get(RF.get(i)), L.get(i + RB.size())); + topsort2index.put(L.get(i + RB.size()), node2index.get(RF.get(i))); + } + } + + @SuppressWarnings("unused") + private List getTopSort() { + List topsort = new ArrayList(); + + for (int i : topsort2index.values()) { + topsort.add(index2node.get(i)); + } + + return topsort; + } + + private void dfsBackward(V node) { + node2mark.put(node, true); + RB.add(node); + + for (V sn : gds.getSourceNodes(node).distinctValues()) { + int top_id = index2topsort.get(node2index.get(sn)); + + if (!node2mark.get(sn) && lower_bound < top_id) + dfsBackward(sn); + } + } + + private void dfsForward(V node) { + node2mark.put(node, true); + RF.add(node); + + for (V tn : gds.getTargetNodes(node).distinctValues()) { + int top_id = index2topsort.get(node2index.get(tn)); + + if (top_id == upper_bound) + System.out.println("!!!Cycle detected!!!"); + else if (!node2mark.get(tn) && top_id < upper_bound) + dfsForward(tn); + } + } + + @Override + public void edgeDeleted(V source, V target) { + // Edge deletion does not affect topsort + } + + @Override + public void nodeInserted(V n) { + node2mark.put(n, false); + node2index.put(n, index); + index2node.put(index, n); + index2topsort.put(index, index); + topsort2index.put(index, index); + index++; + } + + @Override + public void nodeDeleted(V n) { + node2mark.remove(n); + int node_id = node2index.remove(n); + index2node.remove(node_id); + int top_id = index2topsort.remove(node_id); + topsort2index.remove(top_id); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java new file mode 100644 index 00000000..8915998b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; + +/** + * Efficient algorithms to compute the Strongly Connected Components in a directed graph. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the nodes in the graph + */ +public class SCC { + + private SCC() {/*Utility class constructor*/} + + public static long sccId = 0; + + /** + * Computes the SCCs for the given graph and returns them as a multiset. (Iterative version of Tarjan's algorithm) + * + * @param g + * the directed graph data source + * @return the set of SCCs + */ + public static SCCResult computeSCC(IGraphDataSource g) { + int index = 0; + Set> ret = new HashSet>(); + + // stores the lowlink and index information for the given node + Map nodeMap = CollectionsFactory.createMap(); + + // stores all target nodes of a given node - the list will be modified + Map> targetNodeMap = CollectionsFactory.createMap(); + + // stores those target nodes for a given node which have not been visited + Map> notVisitedMap = CollectionsFactory.createMap(); + + // stores the nodes during the traversal + Stack nodeStack = new Stack(); + + // stores the nodes which belong to an scc (there can be many sccs in the stack at the same time) + Stack sccStack = new Stack(); + + boolean sink = false, finishedTraversal = true; + + // initialize all nodes with 0 index and 0 lowlink + Set allNodes = g.getAllNodes(); + for (V n : allNodes) { + nodeMap.put(n, new SCCProperty(0, 0)); + } + + for (V n : allNodes) { + // if the node has not been visited yet + if (nodeMap.get(n).getIndex() == 0) { + nodeStack.push(n); + + while (!nodeStack.isEmpty()) { + V currentNode = nodeStack.peek(); + sink = false; + finishedTraversal = false; + SCCProperty prop = nodeMap.get(currentNode); + + if (nodeMap.get(currentNode).getIndex() == 0) { + index++; + sccStack.push(currentNode); + prop.setIndex(index); + prop.setLowlink(index); + + notVisitedMap.put(currentNode, new HashSet()); + + // storing the target nodes of the actual node + if (g.getTargetNodes(currentNode) != null) { + Set targets = g.getTargetNodes(currentNode).distinctValues(); + targetNodeMap.put(currentNode, CollectionsFactory.createSet(targets)); + } + } + + if (targetNodeMap.get(currentNode) != null) { + + // remove node from stack, the exploration of its children has finished + if (targetNodeMap.get(currentNode).size() == 0) { + targetNodeMap.remove(currentNode); + + nodeStack.pop(); + + for (V targetNode : g.getTargetNodes(currentNode).distinctValues()) { + if (notVisitedMap.get(currentNode).contains(targetNode)) { + prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getLowlink())); + } else if (sccStack.contains(targetNode)) { + prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getIndex())); + } + } + + finishedTraversal = true; + } else { + V targetNode = targetNodeMap.get(currentNode).iterator().next(); + targetNodeMap.get(currentNode).remove(targetNode); + // if the targetNode has not yet been visited push it to the stack + // and mark it in the notVisitedMap + if (nodeMap.get(targetNode).getIndex() == 0) { + notVisitedMap.get(currentNode).add(targetNode); + nodeStack.add(targetNode); + } + } + } + // if currentNode has no target nodes + else { + nodeStack.pop(); + sink = true; + } + + // create scc if node is a sink or an scc has been found + if ((sink || finishedTraversal) && (prop.getLowlink() == prop.getIndex())) { + Set sc = new HashSet(); + V targetNode = null; + + do { + targetNode = sccStack.pop(); + sc.add(targetNode); + } while (!targetNode.equals(currentNode)); + + ret.add(sc); + } + } + } + } + + return new SCCResult(ret, g); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java new file mode 100644 index 00000000..b26e3d37 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; + +public class SCCProperty { + private int index; + private int lowlink; + + public SCCProperty(int index, int lowlink) { + super(); + this.index = index; + this.lowlink = lowlink; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public int getLowlink() { + return lowlink; + } + + public void setLowlink(int lowlink) { + this.lowlink = lowlink; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java new file mode 100644 index 00000000..fde59d3b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; + +import java.util.Map.Entry; +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; + +public class SCCResult { + + private Set> sccs; + private IGraphDataSource gds; + + public SCCResult(Set> sccs, IGraphDataSource gds) { + this.sccs = sccs; + this.gds = gds; + } + + public Set> getSccs() { + return sccs; + } + + public int getSCCCount() { + return sccs.size(); + } + + public double getAverageNodeCount() { + double a = 0; + + for (Set s : sccs) { + a += s.size(); + } + + return a / sccs.size(); + } + + public double getAverageEdgeCount() { + long edgeSum = 0; + + for (Set scc : sccs) { + for (V source : scc) { + for (Entry entry : gds.getTargetNodes(source).entriesWithMultiplicities()) { + if (scc.contains(entry.getKey())) { + edgeSum += entry.getValue(); + } + } + } + } + + return (double) edgeSum / (double) sccs.size(); + } + + public int getBiggestSCCSize() { + int max = 0; + + for (Set scc : sccs) { + if (scc.size() > max) + max = scc.size(); + } + + return max; + } + + public long getSumOfSquares() { + long sum = 0; + + for (Set scc : sccs) { + sum += scc.size() * scc.size(); + } + + return sum; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java new file mode 100644 index 00000000..dd18e6c8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.alg.misc.topsort; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.Stack; + +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; + +/** + * @since 1.6 + */ +public class TopologicalSorting { + + private TopologicalSorting() {/*Utility class constructor*/} + + private static final class Pair { + public T element; + public boolean isParent; + + public Pair(final T element, final boolean isParent) { + this.element = element; + this.isParent = isParent; + } + } + + /** + * Returns a topological ordering for the given graph data source. + * Output format: if there is an a -> b (transitive) reachability, then node a will come before node b in the resulting list. + * + * @param gds the graph data source + * @return a topological ordering + */ + public static List compute(final IGraphDataSource gds) { + final Set visited = new HashSet(); + final LinkedList result = new LinkedList(); + final Stack> dfsStack = new Stack>(); + + for (final T node : gds.getAllNodes()) { + if (!visited.contains(node)) { + dfsStack.push(new Pair(node, false)); + } + + while (!dfsStack.isEmpty()) { + final Pair head = dfsStack.pop(); + final T source = head.element; + + if (head.isParent) { + // we have already seen source, push it to the resulting stack + result.addFirst(source); + } else { + // first time we see source, continue with its children + visited.add(source); + dfsStack.push(new Pair(source, true)); + + for (final T target : gds.getTargetNodes(source).distinctValues()) { + if (!visited.contains(target)) { + dfsStack.push(new Pair(target, false)); + } + } + } + } + } + + return result; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java new file mode 100644 index 00000000..51015404 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java @@ -0,0 +1,140 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.base.itc.alg.representative; + +import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; +import tools.refinery.viatra.runtime.matchers.util.Direction; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public abstract class RepresentativeElectionAlgorithm implements IGraphObserver { + protected final Graph graph; + protected final Map representatives = new HashMap<>(); + protected final Map> components = new HashMap<>(); + private RepresentativeObserver observer; + + protected RepresentativeElectionAlgorithm(Graph graph) { + this.graph = graph; + initializeComponents(); + graph.attachObserver(this); + } + + protected abstract void initializeComponents(); + + protected void initializeSet(Set set) { + var iterator = set.iterator(); + if (!iterator.hasNext()) { + // Set is empty. + return; + } + var representative = iterator.next(); + for (var node : set) { + var oldRepresentative = representatives.put(node, representative); + if (oldRepresentative != null && !representative.equals(oldRepresentative)) { + throw new IllegalStateException("Node %s is already in a set represented by %s, cannot add it to %s" + .formatted(node, oldRepresentative, set)); + } + } + components.put(representative, set); + } + + protected void merge(Object leftRepresentative, Object rightRepresentative) { + if (leftRepresentative.equals(rightRepresentative)) { + return; + } + var leftSet = getComponent(leftRepresentative); + var rightSet = getComponent(rightRepresentative); + if (leftSet.size() < rightSet.size()) { + merge(rightRepresentative, rightSet, leftRepresentative, leftSet); + } else { + merge(leftRepresentative, leftSet, rightRepresentative, rightSet); + } + } + + private void merge(Object preservedRepresentative, Set preservedSet, Object removedRepresentative, + Set removedSet) { + components.remove(removedRepresentative); + for (var node : removedSet) { + representatives.put(node, preservedRepresentative); + preservedSet.add(node); + notifyToObservers(node, removedRepresentative, preservedRepresentative); + } + } + + protected void assignNewRepresentative(Object oldRepresentative, Set set) { + var iterator = set.iterator(); + if (!iterator.hasNext()) { + return; + } + var newRepresentative = iterator.next(); + components.put(newRepresentative, set); + for (var node : set) { + var oldRepresentativeOfNode = representatives.put(node, newRepresentative); + if (!oldRepresentative.equals(oldRepresentativeOfNode)) { + throw new IllegalArgumentException("Node %s was not represented by %s but by %s" + .formatted(node, oldRepresentative, oldRepresentativeOfNode)); + } + notifyToObservers(node, oldRepresentative, newRepresentative); + } + } + + public void setObserver(RepresentativeObserver observer) { + this.observer = observer; + } + + public Map> getComponents() { + return components; + } + + public Object getRepresentative(Object node) { + return representatives.get(node); + } + + public Set getComponent(Object representative) { + return components.get(representative); + } + + public void dispose() { + graph.detachObserver(this); + } + + @Override + public void nodeInserted(Object n) { + var component = new HashSet<>(1); + component.add(n); + initializeSet(component); + notifyToObservers(n, n, Direction.INSERT); + } + + @Override + public void nodeDeleted(Object n) { + var representative = representatives.remove(n); + if (!representative.equals(n)) { + throw new IllegalStateException("Trying to delete node with dangling edges"); + } + components.remove(representative); + notifyToObservers(n, representative, Direction.DELETE); + } + + protected void notifyToObservers(Object node, Object oldRepresentative, Object newRepresentative) { + notifyToObservers(node, oldRepresentative, Direction.DELETE); + notifyToObservers(node, newRepresentative, Direction.INSERT); + } + + protected void notifyToObservers(Object node, Object representative, Direction direction) { + if (observer != null) { + observer.tupleChanged(node, representative, direction); + } + } + + public interface Factory { + RepresentativeElectionAlgorithm create(Graph graph); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java new file mode 100644 index 00000000..93cce1ea --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.base.itc.alg.representative; + +import tools.refinery.viatra.runtime.matchers.util.Direction; + +public interface RepresentativeObserver { + void tupleChanged(Object node, Object representative, Direction direction); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java new file mode 100644 index 00000000..ba42bb13 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.base.itc.alg.representative; + +import tools.refinery.viatra.runtime.base.itc.alg.misc.GraphHelper; +import tools.refinery.viatra.runtime.base.itc.alg.misc.bfs.BFS; +import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; +import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; + +import java.util.Collection; +import java.util.Set; + +public class StronglyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { + public StronglyConnectedComponentAlgorithm(Graph graph) { + super(graph); + } + + @Override + protected void initializeComponents() { + var computedSCCs = SCC.computeSCC(graph).getSccs(); + for (var computedSCC : computedSCCs) { + initializeSet(computedSCC); + } + } + + @Override + public void edgeInserted(Object source, Object target) { + var sourceRoot = getRepresentative(source); + var targetRoot = getRepresentative(target); + if (sourceRoot.equals(targetRoot)) { + // New edge does not change strongly connected components. + return; + } + if (BFS.isReachable(target, source, graph)) { + merge(sourceRoot, targetRoot); + } + } + + @Override + public void edgeDeleted(Object source, Object target) { + var sourceRoot = getRepresentative(source); + var targetRoot = getRepresentative(target); + if (!sourceRoot.equals(targetRoot)) { + // New edge does not change strongly connected components. + return; + } + var component = GraphHelper.getSubGraph(getComponent(sourceRoot), graph); + if (!BFS.isReachable(source, target, component)) { + var newSCCs = SCC.computeSCC(component).getSccs(); + split(sourceRoot, newSCCs); + } + } + + private void split(Object preservedRepresentative, Collection> sets) { + for (var set : sets) { + if (set.contains(preservedRepresentative)) { + components.put(preservedRepresentative, set); + } else { + assignNewRepresentative(preservedRepresentative, set); + } + } + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java new file mode 100644 index 00000000..22159499 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.base.itc.alg.representative; + +import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; + +import java.util.ArrayDeque; +import java.util.HashSet; +import java.util.Set; + +public class WeaklyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { + public WeaklyConnectedComponentAlgorithm(Graph graph) { + super(graph); + } + + @Override + protected void initializeComponents() { + for (var node : graph.getAllNodes()) { + if (representatives.containsKey(node)) { + continue; + } + var reachable = getReachableNodes(node); + initializeSet(reachable); + } + } + + @Override + public void edgeInserted(Object source, Object target) { + var sourceRoot = getRepresentative(source); + var targetRoot = getRepresentative(target); + merge(sourceRoot, targetRoot); + } + + @Override + public void edgeDeleted(Object source, Object target) { + var sourceRoot = getRepresentative(source); + var targetRoot = getRepresentative(target); + if (!sourceRoot.equals(targetRoot)) { + throw new IllegalArgumentException("Trying to remove edge not in graph"); + } + var targetReachable = getReachableNodes(target); + if (!targetReachable.contains(source)) { + split(sourceRoot, targetReachable); + } + } + + private void split(Object sourceRepresentative, Set targetReachable) { + var sourceComponent = getComponent(sourceRepresentative); + sourceComponent.removeAll(targetReachable); + if (targetReachable.contains(sourceRepresentative)) { + components.put(sourceRepresentative, targetReachable); + assignNewRepresentative(sourceRepresentative, sourceComponent); + } else { + assignNewRepresentative(sourceRepresentative, targetReachable); + } + } + + private Set getReachableNodes(Object source) { + var retSet = new HashSet<>(); + retSet.add(source); + var nodeQueue = new ArrayDeque<>(); + nodeQueue.addLast(source); + + while (!nodeQueue.isEmpty()) { + var node = nodeQueue.removeFirst(); + for (var neighbor : graph.getTargetNodes(node).distinctValues()) { + if (!retSet.contains(neighbor)) { + retSet.add(neighbor); + nodeQueue.addLast(neighbor); + } + } + for (var neighbor : graph.getSourceNodes(node).distinctValues()) { + if (!retSet.contains(neighbor)) { + retSet.add(neighbor); + nodeQueue.addLast(neighbor); + } + } + } + + return retSet; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java new file mode 100644 index 00000000..c9b3cafa --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.base.itc.alg.util; + +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; + +/** + * @author Tamas Szabo + * + */ +public class CollectionHelper { + + private CollectionHelper() {/*Utility class constructor*/} + + /** + * Returns the intersection of two sets. It calls {@link Set#retainAll(java.util.Collection)} but returns a new set + * containing the elements of the intersection. + * + * @param set1 + * the first set (can be null, interpreted as empty) + * @param set2 + * the second set (can be null, interpreted as empty) + * @return the intersection of the sets + * @since 1.7 + */ + public static Set intersection(Set set1, Set set2) { + if (set1 == null || set2 == null) + return CollectionsFactory.createSet(); + + Set intersection = CollectionsFactory.createSet(set1); + intersection.retainAll(set2); + return intersection; + } + + + /** + * Returns the difference of two sets (S1\S2). It calls {@link Set#removeAll(java.util.Collection)} but returns a + * new set containing the elements of the difference. + * + * @param set1 + * the first set (can be null, interpreted as empty) + * @param set2 + * the second set (can be null, interpreted as empty) + * @return the difference of the sets + * @since 1.7 + */ + public static Set difference(Set set1, Set set2) { + if (set1 == null) + return CollectionsFactory.createSet(); + + Set difference = CollectionsFactory.createSet(set1); + if (set2 != null) difference.removeAll(set2); + return difference; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java new file mode 100644 index 00000000..0e21f323 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.base.itc.graphimpl; + +import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; +import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCCResult; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +/** + * This class contains utility methods to generate dot representations for {@link Graph} instances. + * + * @author Tamas Szabo + * @since 2.3 + */ +public class DotGenerator { + + private static final String[] colors = new String[] { "yellow", "blue", "red", "green", "gray", "cyan" }; + + private DotGenerator() { + + } + + /** + * Generates the dot representation for the given graph. + * + * @param graph + * the graph + * @param colorSCCs + * specifies if the strongly connected components with size greater than shall be colored + * @param nameFunction + * use this function to provide custom names to nodes, null if the default toString shall be used + * @param colorFunction + * use this function to provide custom color to nodes, null if the default white color shall be used + * @param edgeFunction + * use this function to provide custom edge labels, null if no edge label shall be printed + * @return the dot representation as a string + */ + public static String generateDot(final Graph graph, final boolean colorSCCs, + final Function nameFunction, final Function colorFunction, + final Function> edgeFunction) { + final Map colorMap = new HashMap(); + + if (colorSCCs) { + final SCCResult result = SCC.computeSCC(graph); + final Set> sccs = result.getSccs(); + + int i = 0; + for (final Set scc : sccs) { + if (scc.size() > 1) { + for (final V node : scc) { + final String color = colorMap.get(node); + if (color == null) { + colorMap.put(node, colors[i % colors.length]); + } else { + colorMap.put(node, colorMap.get(node) + ":" + colors[i % colors.length]); + } + } + i++; + } + } + + // if a node has no color yet, then make it white + for (final V node : graph.getAllNodes()) { + if (!colorMap.containsKey(node)) { + colorMap.put(node, "white"); + } + } + } else { + for (final V node : graph.getAllNodes()) { + colorMap.put(node, "white"); + } + } + + if (colorFunction != null) { + for (final V node : graph.getAllNodes()) { + colorMap.put(node, colorFunction.apply(node)); + } + } + + final StringBuilder builder = new StringBuilder(); + builder.append("digraph g {\n"); + + for (final V node : graph.getAllNodes()) { + final String nodePresentation = nameFunction == null ? node.toString() : nameFunction.apply(node); + builder.append("\"" + nodePresentation + "\""); + builder.append("[style=filled,fillcolor=" + colorMap.get(node) + "]"); + builder.append(";\n"); + } + + for (final V source : graph.getAllNodes()) { + final IMemoryView targets = graph.getTargetNodes(source); + if (!targets.isEmpty()) { + final String sourcePresentation = nameFunction == null ? source.toString() : nameFunction.apply(source); + for (final V target : targets.distinctValues()) { + String edgeLabel = null; + if (edgeFunction != null) { + final Function v1 = edgeFunction.apply(source); + if (v1 != null) { + edgeLabel = v1.apply(target); + } + } + + final String targetPresentation = nameFunction == null ? target.toString() + : nameFunction.apply(target); + + builder.append("\"" + sourcePresentation + "\" -> \"" + targetPresentation + "\""); + if (edgeLabel != null) { + builder.append("[label=\"" + edgeLabel + "\"]"); + } + builder.append(";\n"); + } + } + } + + builder.append("}"); + return builder.toString(); + } + + /** + * Generates the dot representation for the given graph. No special pretty printing customization will be applied. + * + * @param graph + * the graph + * @return the dot representation as a string + */ + public static String generateDot(final Graph graph) { + return generateDot(graph, false, null, null, null); + } + + /** + * Returns a simple name shortener function that can be used in the graphviz visualization to help with readability. + * WARNING: if you shorten the name of the {@link Node}s too much, the visualization may become incorrect because + * grahpviz will treat different nodes as the same if their shortened names are the same. + * + * @param maxLength + * the maximum length of the text that is kept from the toString of the objects in the graph + * @return the shrunk toString value + */ + public static Function getNameShortener(final int maxLength) { + return new Function() { + @Override + public String apply(final V obj) { + final String value = obj.toString(); + return value.substring(0, Math.min(value.length(), maxLength)); + } + }; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java new file mode 100644 index 00000000..4267579c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.graphimpl; + +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; +import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class Graph implements IGraphDataSource, IBiDirectionalGraphDataSource { + + // source -> target -> count + private IMultiLookup outgoingEdges; + // target -> source -> count + private IMultiLookup incomingEdges; + + private Set nodes; + + private List> observers; + + public Graph() { + outgoingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); + incomingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); + nodes = CollectionsFactory.createSet(); + observers = CollectionsFactory.createObserverList(); + } + + public void insertEdge(V source, V target) { + outgoingEdges.addPair(source, target); + incomingEdges.addPair(target, source); + + for (IGraphObserver go : observers) { + go.edgeInserted(source, target); + } + } + + /** + * No-op if trying to delete edge that does not exist + * + * @since 2.0 + * @see #deleteEdgeIfExists(Object, Object) + */ + public void deleteEdgeIfExists(V source, V target) { + boolean containedEdge = outgoingEdges.lookupOrEmpty(source).containsNonZero(target); + if (containedEdge) { + deleteEdgeThatExists(source, target); + } + } + + /** + * @throws IllegalStateException + * if trying to delete edge that does not exist + * @since 2.0 + * @see #deleteEdgeIfExists(Object, Object) + */ + public void deleteEdgeThatExists(V source, V target) { + outgoingEdges.removePair(source, target); + incomingEdges.removePair(target, source); + for (IGraphObserver go : observers) { + go.edgeDeleted(source, target); + } + } + + /** + * @deprecated use explicitly {@link #deleteEdgeThatExists(Object, Object)} or + * {@link #deleteEdgeIfExists(Object, Object)} instead. To preserve backwards compatibility, this method + * delegates to the latter. + * + */ + @Deprecated + public void deleteEdge(V source, V target) { + deleteEdgeIfExists(source, target); + } + + /** + * Insert the given node into the graph. + */ + public void insertNode(V node) { + if (nodes.add(node)) { + for (IGraphObserver go : observers) { + go.nodeInserted(node); + } + } + } + + /** + * Deletes the given node AND all of the edges going in and out from the node. + */ + public void deleteNode(V node) { + if (nodes.remove(node)) { + IMemoryView incomingView = incomingEdges.lookup(node); + if (incomingView != null) { + Map incoming = CollectionsFactory.createMap(incomingView.asMap()); + + for (Entry entry : incoming.entrySet()) { + for (int i = 0; i < entry.getValue(); i++) { + deleteEdgeThatExists(entry.getKey(), node); + } + } + } + + IMemoryView outgoingView = outgoingEdges.lookup(node); + if (outgoingView != null) { + Map outgoing = CollectionsFactory.createMap(outgoingView.asMap()); + + for (Entry entry : outgoing.entrySet()) { + for (int i = 0; i < entry.getValue(); i++) { + deleteEdgeThatExists(node, entry.getKey()); + } + } + } + + for (IGraphObserver go : observers) { + go.nodeDeleted(node); + } + } + } + + @Override + public void attachObserver(IGraphObserver go) { + observers.add(go); + } + + @Override + public void attachAsFirstObserver(IGraphObserver observer) { + observers.add(0, observer); + } + + @Override + public void detachObserver(IGraphObserver go) { + observers.remove(go); + } + + @Override + public Set getAllNodes() { + return nodes; + } + + @Override + public IMemoryView getTargetNodes(V source) { + return outgoingEdges.lookupOrEmpty(source); + } + + @Override + public IMemoryView getSourceNodes(V target) { + return incomingEdges.lookupOrEmpty(target); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("nodes = "); + for (V n : getAllNodes()) { + sb.append(n.toString()); + sb.append(" "); + } + sb.append(" edges = "); + for (V source : outgoingEdges.distinctKeys()) { + IMemoryView targets = outgoingEdges.lookup(source); + for (V target : targets.distinctValues()) { + int count = targets.getCount(target); + for (int i = 0; i < count; i++) { + sb.append("(" + source + "," + target + ") "); + } + } + } + return sb.toString(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java new file mode 100644 index 00000000..64659447 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.igraph; + +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiset; + +/** + * A bi-directional graph data source supports all operations that an {@link IGraphDataSource} does, but it + * also makes it possible to query the incoming edges of nodes, not only the outgoing edges. + * + * @author Tamas Szabo + * + * @param the type of the nodes in the graph + */ +public interface IBiDirectionalGraphDataSource extends IGraphDataSource { + + /** + * Returns the source nodes for the given target node. + * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source. + * + * The method must not return null. + * + * @param target the target node + * @return the multiset of source nodes + * @since 2.0 + */ + public IMemoryView getSourceNodes(V target); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java new file mode 100644 index 00000000..becab0eb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.igraph; + +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; + +/** + * This class can be used to wrap an {@link IGraphDataSource} into an {@link IBiDirectionalGraphDataSource}. This class + * provides support for the retrieval of source nodes for a given target which is not supported by standard + * {@link IGraphDataSource} implementations. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the nodes in the graph data source + */ +public class IBiDirectionalWrapper implements IBiDirectionalGraphDataSource, IGraphObserver { + + private IGraphDataSource wrappedDataSource; + // target -> source -> count + private IMultiLookup incomingEdges; + + public IBiDirectionalWrapper(IGraphDataSource gds) { + this.wrappedDataSource = gds; + + this.incomingEdges = CollectionsFactory.createMultiLookup( + Object.class, MemoryType.MULTISETS, Object.class); + + if (gds.getAllNodes() != null) { + for (V source : gds.getAllNodes()) { + IMemoryView targets = gds.getTargetNodes(source); + for (V target : targets.distinctValues()) { + int count = targets.getCount(target); + for (int i = 0; i < count; i++) { + edgeInserted(source, target); + } + } + } + } + + gds.attachAsFirstObserver(this); + } + + @Override + public void attachObserver(IGraphObserver observer) { + wrappedDataSource.attachObserver(observer); + } + + @Override + public void attachAsFirstObserver(IGraphObserver observer) { + wrappedDataSource.attachAsFirstObserver(observer); + } + + @Override + public void detachObserver(IGraphObserver observer) { + wrappedDataSource.detachObserver(observer); + } + + @Override + public Set getAllNodes() { + return wrappedDataSource.getAllNodes(); + } + + @Override + public IMemoryView getTargetNodes(V source) { + return wrappedDataSource.getTargetNodes(source); + } + + @Override + public IMemoryView getSourceNodes(V target) { + return incomingEdges.lookupOrEmpty(target); + } + + @Override + public void edgeInserted(V source, V target) { + incomingEdges.addPair(target, source); + } + + @Override + public void edgeDeleted(V source, V target) { + incomingEdges.removePair(target, source); + } + + @Override + public void nodeInserted(V n) { + + } + + @Override + public void nodeDeleted(V node) { + + } + + @Override + public String toString() { + return wrappedDataSource.toString(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java new file mode 100644 index 00000000..3fa65936 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.igraph; + +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiset; + +import java.util.Set; + +/** + * The interface prescribes the set of operations that a graph data source must support. + *

Note that the old version of the interface is broken at version 1.6; + * MultiSets are now presented as Maps instead of Lists. + * + * @author Tamas Szabo + * + * @param + * the type of the nodes in the graph + */ +public interface IGraphDataSource { + + /** + * Attaches a new graph observer to this graph data source. Observers will be notified in the order they have been registered. + * + * @param observer the graph observer + */ + public void attachObserver(IGraphObserver observer); + + /** + * Attaches a new graph observer to this graph data source as the first one. + * In the notification order this observer will be the first one as long as another call to this method happens. + * + * @param observer the graph observer + * @since 1.6 + */ + public void attachAsFirstObserver(IGraphObserver observer); + + /** + * Detaches an already registered graph observer from this graph data source. + * + * @param observer the graph observer + */ + public void detachObserver(IGraphObserver observer); + + /** + * Returns the complete set of nodes in the graph data source. + * + * @return the set of all nodes + */ + public Set getAllNodes(); + + /** + * Returns the target nodes for the given source node. + * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source. + * + * The method must not return null. + * + * @param source the source node + * @return the multiset of target nodes + * @since 2.0 + */ + public IMemoryView getTargetNodes(V source); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java new file mode 100644 index 00000000..5cb2d9fa --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.igraph; + +/** + * Interface GraphObserver is used to observ the changes in a graph; edge and node insertion/deleteion. + * + * @author Tamas Szabo + * + */ +public interface IGraphObserver { + + /** + * Used to notify when an edge is inserted into the graph. + * + * @param source + * the source of the edge + * @param target + * the target of the edge + */ + public void edgeInserted(V source, V target); + + /** + * Used to notify when an edge is deleted from the graph. + * + * @param source + * the source of the edge + * @param target + * the target of the edge + */ + public void edgeDeleted(V source, V target); + + /** + * Used to notify when a node is inserted into the graph. + * + * @param n + * the node + */ + public void nodeInserted(V n); + + /** + * Used to notify when a node is deleted from the graph. + * + * @param n + * the node + */ + public void nodeDeleted(V n); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java new file mode 100644 index 00000000..5924b723 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.igraph; + +import java.util.Set; + +import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; + +/** + * This interface defines those methods that a transitive reachability data source should provide. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the node + */ +public interface ITcDataSource { + + /** + * Attach a transitive closure relation observer. + * + * @param to + * the observer object + */ + public void attachObserver(ITcObserver to); + + /** + * Detach a transitive closure relation observer. + * + * @param to + * the observer object + */ + public void detachObserver(ITcObserver to); + + /** + * Returns all nodes which are reachable from the source node. + * + * @param source + * the source node + * @return the set of target nodes + */ + public Set getAllReachableTargets(V source); + + /** + * Returns all nodes from which the target node is reachable. + * + * @param target + * the target node + * @return the set of source nodes + */ + public Set getAllReachableSources(V target); + + /** + * Returns true if the target node is reachable from the source node. + * + * @param source + * the source node + * @param target + * the target node + * @return true if target is reachable from source, false otherwise + */ + public boolean isReachable(V source, V target); + + /** + * The returned {@link IGraphPathFinder} can be used to retrieve paths between nodes using transitive reachability. + * + * @return a path finder for the graph. + */ + public IGraphPathFinder getPathFinder(); + + /** + * Call this method to properly dispose the data structures of a transitive closure algorithm. + */ + public void dispose(); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java new file mode 100644 index 00000000..fded53f1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.base.itc.igraph; + +/** + * Interface ITcObserver is used to observe the changes in a transitive closure relation; tuple insertion/deletion. + * + * @author Szabo Tamas + * + */ +public interface ITcObserver { + + /** + * Used to notify when a tuple is inserted into the transitive closure relation. + * + * @param source + * the source of the tuple + * @param target + * the target of the tuple + */ + public void tupleInserted(V source, V target); + + /** + * Used to notify when a tuple is deleted from the transitive closure relation. + * + * @param source + * the source of the tuple + * @param target + * the target of the tuple + */ + public void tupleDeleted(V source, V target); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java new file mode 100644 index 00000000..83f6f766 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/ViatraQueryRuntimeException.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers; + +/** + * A common base class for all exceptions thrown by various VIATRA Query Runtime APIs. + * + * @author Zoltan Ujhelyi + * @since 2.0 + */ +public abstract class ViatraQueryRuntimeException extends RuntimeException { + + private static final long serialVersionUID = -8505253058035069310L; + + public ViatraQueryRuntimeException() { + super(); + } + + public ViatraQueryRuntimeException(String message) { + super(message); + } + + public ViatraQueryRuntimeException(Throwable cause) { + super(cause); + } + + public ViatraQueryRuntimeException(String message, Throwable cause) { + super(message, cause); + } + + public ViatraQueryRuntimeException(String message, Throwable cause, boolean enableSuppression, + boolean writableStackTrace) { + super(message, cause, enableSuppression, writableStackTrace); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java new file mode 100644 index 00000000..2c09ede1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/AverageAccumulator.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +/** + * @since 2.0 + */ +class AverageAccumulator { + Domain value; + long count; + + public AverageAccumulator(Domain value, long count) { + super(); + this.value = value; + this.count = count; + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java new file mode 100644 index 00000000..e8a26afd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleAverageOperator.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import java.util.OptionalDouble; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; + +/** + * @author Zoltan Ujhelyi + * @since 2.0 + */ +public class DoubleAverageOperator implements IMultisetAggregationOperator, Double> { + + public static final DoubleAverageOperator INSTANCE = new DoubleAverageOperator(); + + private DoubleAverageOperator() { + // Singleton, do not call. + } + + @Override + public String getShortDescription() { + return "avg incrementally computes the average of java.lang.Integer values"; + } + + @Override + public String getName() { + return "avg"; + } + + @Override + public AverageAccumulator createNeutral() { + return new AverageAccumulator(0d, 0l); + } + + @Override + public boolean isNeutral(AverageAccumulator result) { + return result.count == 0l; + } + + @Override + public AverageAccumulator update(AverageAccumulator oldResult, Double updateValue, + boolean isInsertion) { + if (isInsertion) { + oldResult.value += updateValue; + oldResult.count++; + } else { + oldResult.value -= updateValue; + oldResult.count--; + } + return oldResult; + } + + @Override + public Double getAggregate(AverageAccumulator result) { + return (result.count == 0) + ? null + : result.value/result.count; + } + + @Override + public Double aggregateStream(Stream stream) { + final OptionalDouble averageOpt = stream.mapToDouble(Double::doubleValue).average(); + return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; + } + + /** + * @since 2.4 + */ + @Override + public AverageAccumulator clone(AverageAccumulator original) { + return new AverageAccumulator(original.value, original.count); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java new file mode 100644 index 00000000..744b0cd1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/DoubleSumOperator.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; + +/** + * Incrementally computes the sum of java.lang.Double values + * @author Gabor Bergmann + * @since 1.4 + */ +public class DoubleSumOperator extends AbstractMemorylessAggregationOperator { + public static final DoubleSumOperator INSTANCE = new DoubleSumOperator(); + + private DoubleSumOperator() { + // Singleton, do not call. + } + + @Override + public String getShortDescription() { + return "sum incrementally computes the sum of java.lang.Double values"; + } + @Override + public String getName() { + return "sum"; + } + + @Override + public Double createNeutral() { + return 0d; + } + + @Override + public boolean isNeutral(Double result) { + return createNeutral().equals(result); + } + + @Override + public Double update(Double oldResult, Double updateValue, boolean isInsertion) { + return isInsertion ? + oldResult + updateValue : + oldResult - updateValue; + } + + /** + * @since 2.0 + */ + @Override + public Double aggregateStream(Stream stream) { + return stream.mapToDouble(Double::doubleValue).sum(); + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java new file mode 100644 index 00000000..ee4ceeb8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/ExtremumOperator.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import java.util.Comparator; +import java.util.SortedMap; +import java.util.TreeMap; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; + +/** + * Incrementally computes the minimum or maximum of java.lang.Comparable values, using the default comparison + * + * @author Gabor Bergmann + * @since 1.4 + */ +public class ExtremumOperator> + implements IMultisetAggregationOperator, T> { + + public enum Extreme { + MIN, MAX; + + /** + * @since 2.0 + */ + public T pickFrom(SortedMap nonEmptyMultiSet) { + switch(this) { + case MIN: + return nonEmptyMultiSet.firstKey(); + case MAX: + return nonEmptyMultiSet.lastKey(); + default: + return null; + } + } + } + + private static final ExtremumOperator MIN_OP = new ExtremumOperator<>(Extreme.MIN); + private static final ExtremumOperator MAX_OP = new ExtremumOperator<>(Extreme.MAX); + + public static > ExtremumOperator getMin() { + return MIN_OP; + } + public static > ExtremumOperator getMax() { + return MAX_OP; + } + + Extreme extreme; + private ExtremumOperator(Extreme extreme) { + super(); + this.extreme = extreme; + } + + @Override + public String getShortDescription() { + String opName = getName(); + return String.format( + "%s incrementally computes the %simum of java.lang.Comparable values, using the default comparison", + opName, opName); + } + + @Override + public String getName() { + return extreme.name().toLowerCase(); + } + + /** + * @since 2.0 + */ + @Override + public SortedMap createNeutral() { + return new TreeMap<>(); + } + + /** + * @since 2.0 + */ + @Override + public boolean isNeutral(SortedMap result) { + return result.isEmpty(); + } + + /** + * @since 2.0 + */ + @Override + public SortedMap update(SortedMap oldResult, T updateValue, boolean isInsertion) { + oldResult.compute(updateValue, (value, c) -> { + int count = (c == null) ? 0 : c; + int result = (isInsertion) ? count+1 : count-1; + return (result == 0) ? null : result; + }); + return oldResult; + } + + /** + * @since 2.0 + */ + @Override + public T getAggregate(SortedMap result) { + return result.isEmpty() ? null : + extreme.pickFrom(result); + } + + /** + * @since 2.0 + */ + @Override + public T aggregateStream(Stream stream) { + switch (extreme) { + case MIN: + return stream.min(Comparator.naturalOrder()).orElse(null); + case MAX: + return stream.max(Comparator.naturalOrder()).orElse(null); + default: + return null; + } + } + + /** + * @since 2.4 + */ + @Override + public SortedMap clone(SortedMap original) { + return new TreeMap(original); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java new file mode 100644 index 00000000..bf422476 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerAverageOperator.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import java.util.OptionalDouble; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; + +/** + * @author Zoltan Ujhelyi + * @since 2.0 + */ +public class IntegerAverageOperator implements IMultisetAggregationOperator, Double> { + + public static final IntegerAverageOperator INSTANCE = new IntegerAverageOperator(); + + private IntegerAverageOperator() { + // Singleton, do not call. + } + + @Override + public String getShortDescription() { + return "avg incrementally computes the average of java.lang.Integer values"; + } + + @Override + public String getName() { + return "avg"; + } + + @Override + public AverageAccumulator createNeutral() { + return new AverageAccumulator(0, 0l); + } + + @Override + public boolean isNeutral(AverageAccumulator result) { + return result.count == 0l; + } + + @Override + public AverageAccumulator update(AverageAccumulator oldResult, Integer updateValue, + boolean isInsertion) { + if (isInsertion) { + oldResult.value += updateValue; + oldResult.count++; + } else { + oldResult.value -= updateValue; + oldResult.count--; + } + return oldResult; + } + + @Override + public Double getAggregate(AverageAccumulator result) { + return (result.count == 0) + ? null + : ((double)result.value)/result.count; + } + + @Override + public Double aggregateStream(Stream stream) { + final OptionalDouble averageOpt = stream.mapToInt(Integer::intValue).average(); + return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; + } + + /** + * @since 2.4 + */ + @Override + public AverageAccumulator clone(AverageAccumulator original) { + return new AverageAccumulator(original.value, original.count); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java new file mode 100644 index 00000000..18584256 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/IntegerSumOperator.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; + +/** + * Incrementally computes the sum of java.lang.Integer values + * @author Gabor Bergmann + * @since 1.4 + */ +public class IntegerSumOperator extends AbstractMemorylessAggregationOperator { + public static final IntegerSumOperator INSTANCE = new IntegerSumOperator(); + + private IntegerSumOperator() { + // Singleton, do not call. + } + + @Override + public String getShortDescription() { + return "sum incrementally computes the sum of java.lang.Integer values"; + } + @Override + public String getName() { + return "sum"; + } + + @Override + public Integer createNeutral() { + return 0; + } + + @Override + public boolean isNeutral(Integer result) { + return createNeutral().equals(result); + } + + @Override + public Integer update(Integer oldResult, Integer updateValue, boolean isInsertion) { + return isInsertion ? + oldResult + updateValue : + oldResult - updateValue; + } + + /** + * @since 2.0 + */ + @Override + public Integer aggregateStream(Stream stream) { + return stream.mapToInt(Integer::intValue).sum(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java new file mode 100644 index 00000000..d56c9507 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongAverageOperator.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import java.util.OptionalDouble; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IMultisetAggregationOperator; + +/** + * @author Zoltan Ujhelyi + * @since 2.0 + */ +public class LongAverageOperator implements IMultisetAggregationOperator, Double> { + + public static final LongAverageOperator INSTANCE = new LongAverageOperator(); + + private LongAverageOperator() { + // Singleton, do not call. + } + + @Override + public String getShortDescription() { + return "avg incrementally computes the average of java.lang.Integer values"; + } + + @Override + public String getName() { + return "avg"; + } + + @Override + public AverageAccumulator createNeutral() { + return new AverageAccumulator(0l, 0l); + } + + @Override + public boolean isNeutral(AverageAccumulator result) { + return result.count == 0l; + } + + @Override + public AverageAccumulator update(AverageAccumulator oldResult, Long updateValue, + boolean isInsertion) { + if (isInsertion) { + oldResult.value += updateValue; + oldResult.count++; + } else { + oldResult.value -= updateValue; + oldResult.count--; + } + return oldResult; + } + + @Override + public Double getAggregate(AverageAccumulator result) { + return (result.count == 0) + ? null + : ((double)result.value)/result.count; + } + + @Override + public Double aggregateStream(Stream stream) { + final OptionalDouble averageOpt = stream.mapToLong(Long::longValue).average(); + return averageOpt.isPresent() ? averageOpt.getAsDouble() : null; + } + + /** + * @since 2.4 + */ + @Override + public AverageAccumulator clone(AverageAccumulator original) { + return new AverageAccumulator(original.value, original.count); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java new file mode 100644 index 00000000..29ded090 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/LongSumOperator.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AbstractMemorylessAggregationOperator; + +/** + * Incrementally computes the sum of java.lang.Long values + * @author Gabor Bergmann + * @since 1.4 + */ +public class LongSumOperator extends AbstractMemorylessAggregationOperator { + public static final LongSumOperator INSTANCE = new LongSumOperator(); + + private LongSumOperator() { + // Singleton, do not call. + } + + @Override + public String getShortDescription() { + return "sum incrementally computes the sum of java.lang.Long values"; + } + @Override + public String getName() { + return "sum"; + } + + @Override + public Long createNeutral() { + return 0L; + } + + @Override + public boolean isNeutral(Long result) { + return createNeutral().equals(result); + } + + @Override + public Long update(Long oldResult, Long updateValue, boolean isInsertion) { + return isInsertion ? + oldResult + updateValue : + oldResult - updateValue; + } + + /** + * @since 2.0 + */ + @Override + public Long aggregateStream(Stream stream) { + return stream.mapToLong(Long::longValue).sum(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java new file mode 100644 index 00000000..c25678aa --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/avg.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; + +/** + * This aggregator calculates the average of the values of a selected aggregate parameter of a called pattern. The aggregate + * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The + * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the + * summation. + * + * @since 2.0 + * + */ +@AggregatorType( + parameterTypes = {Integer.class, Double.class, Long.class}, + returnTypes = {Double.class, Double.class, Double.class}) +public final class avg implements IAggregatorFactory { + + @Override + public BoundAggregator getAggregatorLogic(Class domainClass) { + if (Integer.class.equals(domainClass)) + return new BoundAggregator(IntegerAverageOperator.INSTANCE, Integer.class, Double.class); + if (Double.class.equals(domainClass)) + return new BoundAggregator(DoubleAverageOperator.INSTANCE, Double.class, Double.class); + if (Long.class.equals(domainClass)) + return new BoundAggregator(LongAverageOperator.INSTANCE, Long.class, Double.class); + else throw new IllegalArgumentException(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java new file mode 100644 index 00000000..8310a0ce --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/count.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; + +/** + * An aggregator to count the number of matches a pattern has. The return of the aggregator is an non-negative integer + * number. + * + * @since 1.4 + * + */ +@AggregatorType(parameterTypes = {Void.class}, returnTypes = {Integer.class}) +public final class count implements IAggregatorFactory { + + @Override + public BoundAggregator getAggregatorLogic(Class domainClass) { + if (Void.class.equals(domainClass)) + return new BoundAggregator(null, Void.class, Integer.class); + else throw new IllegalArgumentException(); + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java new file mode 100644 index 00000000..e0236223 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/max.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Calendar; +import java.util.Date; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; + +/** + * This aggregator calculates the maximum value of a selected aggregate parameter of a called pattern. The aggregate + * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The + * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the + * minimum calculation. + * + * @since 1.4 + * @author Gabor Bergmann + */ +@AggregatorType( + // TODO T extends Comparable? + parameterTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, + Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}, + returnTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, + Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}) +public final class max implements IAggregatorFactory { + + @Override + public BoundAggregator getAggregatorLogic(Class domainClass) { + if (Comparable.class.isAssignableFrom(domainClass)) + return new BoundAggregator(ExtremumOperator.getMax(), domainClass, domainClass); + else throw new IllegalArgumentException(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java new file mode 100644 index 00000000..6408c57b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/min.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Calendar; +import java.util.Date; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; + +/** + * This aggregator calculates the minimum value of a selected aggregate parameter of a called pattern. The aggregate + * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The + * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the + * minimum calculation. + * + * @since 1.4 + * + */ +@AggregatorType( + // TODO T extends Comparable? + parameterTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, + Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}, + returnTypes = {BigDecimal.class, BigInteger.class, Boolean.class, Byte.class, Calendar.class, Character.class, + Date.class, Double.class, Enum.class, Float.class, Integer.class, Long.class, Short.class, String.class}) +public final class min implements IAggregatorFactory { + + @Override + public BoundAggregator getAggregatorLogic(Class domainClass) { + if (Comparable.class.isAssignableFrom(domainClass)) + return new BoundAggregator(ExtremumOperator.getMin(), domainClass, domainClass); + else throw new IllegalArgumentException(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java new file mode 100644 index 00000000..69ff2e75 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/aggregators/sum.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.aggregators; + +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.AggregatorType; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.IAggregatorFactory; + +/** + * This aggregator calculates the sum of the values of a selected aggregate parameter of a called pattern. The aggregate + * parameter is selected with the '#' symbol; the aggregate parameter must not be used outside the aggregator call. The + * other parameters of the call might be bound or unbound; bound parameters limit the matches to consider for the + * summation. + * + * @since 1.4 + * + */ +@AggregatorType( + parameterTypes = {Integer.class, Double.class, Long.class}, + returnTypes = {Integer.class, Double.class, Long.class}) +public final class sum implements IAggregatorFactory { + + @Override + public BoundAggregator getAggregatorLogic(Class domainClass) { + if (Integer.class.equals(domainClass)) + return new BoundAggregator(IntegerSumOperator.INSTANCE, Integer.class, Integer.class); + if (Double.class.equals(domainClass)) + return new BoundAggregator(DoubleSumOperator.INSTANCE, Double.class, Double.class); + if (Long.class.equals(domainClass)) + return new BoundAggregator(LongSumOperator.INSTANCE, Long.class, Long.class); + else throw new IllegalArgumentException(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java new file mode 100644 index 00000000..1917e909 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/OrderedIterableMerge.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.algorithms; + +import java.util.Comparator; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * @author Gabor Bergmann + * @since 2.1 + * + */ +public class OrderedIterableMerge { + + private OrderedIterableMerge() { + // Hidden utility class constructor + } + + /** + * Lazily merges two iterables, each ordered according to a given comparator. + * Retains order in the result, and also eliminates any duplicates that appear in both arguments. + */ + public static Iterable mergeUniques(Iterable first, Iterable second, Comparator comparator) { + return () -> new Iterator() { + Iterator firstIterator = first.iterator(); + Iterator secondIterator = second.iterator(); + T firstItem; + T secondItem; + + { + fetchFirst(); + fetchSecond(); + } + + private T fetchFirst() { + T previous = firstItem; + if (firstIterator.hasNext()) + firstItem = firstIterator.next(); + else + firstItem = null; + return previous; + } + private T fetchSecond() { + T previous = secondItem; + if (secondIterator.hasNext()) + secondItem = secondIterator.next(); + else + secondItem = null; + return previous; + } + + @Override + public boolean hasNext() { + return firstItem != null || secondItem != null; + } + @Override + public T next() { + if (!hasNext()) throw new NoSuchElementException(); + if (firstItem != null && secondItem != null) { + if (secondItem == firstItem) { // duplicates + fetchFirst(); + return fetchSecond(); + } else if (comparator.compare(firstItem, secondItem) < 0) { + return fetchFirst(); + } else { + return fetchSecond(); + } + } else if (firstItem != null) { + return fetchFirst(); + } else { // secondItem must be non-null + return fetchSecond(); + } + } + }; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java new file mode 100644 index 00000000..c69f08e5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFind.java @@ -0,0 +1,214 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.algorithms; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; + +/** + * Union-find data structure implementation. Note that the implementation relies on the correct implementation of the + * equals method of the type parameter's class. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the element's stored in the union-find data structure + */ +public class UnionFind { + + private final Map> nodeMap; + final Map> setMap; + + /** + * Instantiate a new union-find data structure. + */ + public UnionFind() { + nodeMap = CollectionsFactory.createMap(); + setMap = CollectionsFactory.createMap(); + } + + /** + * Instantiate a new union-find data structure with the given elements as separate sets. + */ + public UnionFind(Iterable elements) { + this(); + for (V element : elements) { + makeSet(element); + } + } + + /** + * Creates a new union set from a collection of elements. + * + * @param nodes + * the collection of elements + * @return the root element + */ + public V makeSet(Collection nodes) { + if (!nodes.isEmpty()) { + Iterator iterator = nodes.iterator(); + V root = makeSet(iterator.next()); + while (iterator.hasNext()) { + root = union(root, iterator.next()); + } + return root; + } else { + return null; + } + } + + /** + * This method creates a single set containing the given node. + * + * @param node + * the root node of the set + * @return the root element + */ + public V makeSet(V node) { + if (!nodeMap.containsKey(node)) { + UnionFindNodeProperty prop = new UnionFindNodeProperty(0, node); + nodeMap.put(node, prop); + Set set = new HashSet(); + set.add(node); + setMap.put(node, set); + } + return node; + } + + /** + * Find method with path compression. + * + * @param node + * the node to find + * @return the root node of the set in which the given node can be found + */ + public V find(V node) { + UnionFindNodeProperty prop = nodeMap.get(node); + + if (prop != null) { + if (prop.parent.equals(node)) { + return node; + } else { + prop.parent = find(prop.parent); + return prop.parent; + } + } + return null; + } + + /** + * Union by rank implementation of the two sets which contain x and y; x and/or y can be a single element from the + * universe. + * + * @param x + * set or single element of the universe + * @param y + * set or single element of the universe + * @return the new root of the two sets + */ + public V union(V x, V y) { + V xRoot = find(x); + V yRoot = find(y); + + if ((xRoot == null) || (yRoot == null)) { + return union( (xRoot == null) ? makeSet(x) : xRoot, (yRoot == null) ? makeSet(y) : yRoot); + } + else if (!xRoot.equals(yRoot)) { + UnionFindNodeProperty xRootProp = nodeMap.get(xRoot); + UnionFindNodeProperty yRootProp = nodeMap.get(yRoot); + + if (xRootProp.rank < yRootProp.rank) { + xRootProp.parent = yRoot; + setMap.get(yRoot).addAll(setMap.get(xRoot)); + setMap.remove(xRoot); + return yRoot; + } else {// (xRootProp.rank >= yRootProp.rank) + yRootProp.parent = xRoot; + yRootProp.rank = (xRootProp.rank == yRootProp.rank) ? yRootProp.rank + 1 : yRootProp.rank; + setMap.get(xRoot).addAll(setMap.get(yRoot)); + setMap.remove(yRoot); + return xRoot; + } + } else { + return xRoot; + } + } + + /** + * Places the given elements in to the same partition. + */ + public void unite(Set elements) { + if (elements.size() > 1) { + V current = null; + for (V element : elements) { + if (current != null) { + if (getPartition(element) != null) { + union(current, element); + } + } else { + if (getPartition(element) != null) { + current = element; + } + } + } + } + } + + /** + * Delete the set whose root is the given node. + * + * @param root + * the root node + */ + public void deleteSet(V root) { + // if (setMap.containsKey(root)) + for (V n : setMap.get(root)) { + nodeMap.remove(n); + } + setMap.remove(root); + } + + /** + * Returns if all given elements are in the same partition. + */ + public boolean isSameUnion(Set elements) { + for (Set partition : setMap.values()) { + if (partition.containsAll(elements)) { + return true; + } + } + return false; + } + + + /** + * Returns the partition in which the given element can be found, or null otherwise. + */ + public Set getPartition(V element) { + V root = find(element); + return setMap.get(root); + } + + /** + * Returns all partitions. + */ + public Collection> getPartitions() { + return setMap.values(); + } + + public Set getPartitionHeads() { + return setMap.keySet(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java new file mode 100644 index 00000000..82852f9c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/algorithms/UnionFindNodeProperty.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.algorithms; + +public class UnionFindNodeProperty { + + public int rank; + public V parent; + + public UnionFindNodeProperty() { + this.rank = 0; + this.parent = null; + } + + public UnionFindNodeProperty(int rank, V parent) { + super(); + this.rank = rank; + this.parent = parent; + } + + @Override + public String toString() { + return "[rank:" + rank + ", parent:" + parent.toString() + "]"; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java new file mode 100644 index 00000000..317293bf --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/CommonQueryHintOptions.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IRewriterTraceCollector; +import tools.refinery.viatra.runtime.matchers.psystem.rewriters.NopTraceCollector; + +/** + * Query evaluation hints applicable to any engine + * @since 1.6 + * + */ +public final class CommonQueryHintOptions { + + private CommonQueryHintOptions() { + // Hiding constructor for utility class + } + + /** + * This hint instructs the query backends to record trace information into the given trace collector + */ + public static final QueryHintOption normalizationTraceCollector = + hintOption("normalizationTraceCollector", NopTraceCollector.INSTANCE); + + // internal helper for conciseness + private static QueryHintOption hintOption(String hintKeyLocalName, T defaultValue) { + return new QueryHintOption<>(CommonQueryHintOptions.class, hintKeyLocalName, defaultValue); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java new file mode 100644 index 00000000..40853f46 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ICallDelegationStrategy.java @@ -0,0 +1,89 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; +import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; + +/** + * Function object that specifies how hints (including backend preferences) shall propagate through pattern calls. + * + *

A few useful default implementations are included as static fields. + * + *

As of 2.1, only suppported by the local search backend, and only if the pattern call is not flattened. + * + * @author Gabor Bergmann + * @since 2.1 + */ +@FunctionalInterface +public interface ICallDelegationStrategy { + + /** + * Specifies how hints (including backend preferences) shall propagate through pattern calls. + * + * @param call a {@link PConstraint} in a query that calls another query. + * @param callerHint a hint under which the calling pattern is evaluated, + * @param callerBackend the actual backend evaluating the calling pattern. + * @param calleeHintProvider the provider of hints for the called pattern. + * @return the hints, including backend selection, + * that the backend responsible for the caller pattern must specify when + * requesting the {@link IQueryResultProvider} for the called pattern via {@link IQueryResultProviderAccess}. + */ + public QueryEvaluationHint transformHints(IQueryReference call, + QueryEvaluationHint callerHint, + IQueryBackend callerBackend, + IQueryBackendHintProvider calleeHintProvider); + + + /** + * Options known for callee are used to override caller options, except the backend selection. + * Always use the same backend for the callee and the caller, regardless what is specified for the callee pattern. + * @author Gabor Bergmann + */ + public static final ICallDelegationStrategy FULL_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { + QueryEvaluationHint calleeHint = + calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); + QueryEvaluationHint result = + callerHint == null ? calleeHint : callerHint.overrideBy(calleeHint); + + QueryEvaluationHint backendAdhesion = new QueryEvaluationHint( + null /* settings-ignorant */, callerBackend.getFactory()); + result = result.overrideBy(backendAdhesion); + return result; + }; + /** + * Options known for callee are used to override caller options, including the backend selection. + * If callee does not specify a backend requirement, the backend of the caller is kept. + * @author Gabor Bergmann + */ + public static final ICallDelegationStrategy PARTIAL_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { + QueryEvaluationHint backendAdhesion = new QueryEvaluationHint( + null /* settings-ignorant */, callerBackend.getFactory()); + + QueryEvaluationHint result = + callerHint == null ? backendAdhesion : callerHint.overrideBy(backendAdhesion); + + QueryEvaluationHint calleeHint = calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); + result = result.overrideBy(calleeHint); + + return result; + }; + /** + * Options known for callee are used to override caller options, including the backend selection. + * Always use the backend specified for the callee (or the default if none), regardless of the backend of the caller. + * @author Gabor Bergmann + */ + public static final ICallDelegationStrategy NO_BACKEND_ADHESION = (call, callerHint, callerBackend, calleeHintProvider) -> { + QueryEvaluationHint calleeHint = calleeHintProvider.getQueryEvaluationHint(call.getReferredQuery()); + return callerHint == null ? calleeHint : callerHint.overrideBy(calleeHint); + }; + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java new file mode 100644 index 00000000..104b68a8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IMatcherCapability.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +/** + * Implementations of this interface can be used to decide whether a matcher created by an arbitrary backend can + * potentially be used as a substitute for another matcher. + * + * @author Grill Balázs + * @since 1.4 + * + */ +public interface IMatcherCapability { + + /** + * Returns true if matchers of this capability can be used as a substitute for a matcher implementing the given capability + */ + public boolean canBeSubstitute(IMatcherCapability capability); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java new file mode 100644 index 00000000..c85f10a4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackend.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * Internal interface for a VIATRA query specification. Each query is associated with a pattern. Methods instantiate a matcher + * of the pattern with various parameters. + * + * @author Bergmann Gábor + * @since 0.9 + * @noextend This interface is not intended to be extended by users of the VIATRA framework, and should only be used by the query engine + */ +public interface IQueryBackend { + + /** + * @return true iff this backend is incremental, i.e. it caches the results of queries for quick retrieval, + * and can provide update notifications on result set changes. + */ + public boolean isCaching(); + + /** + * Returns a result provider for a given query. Repeated calls may return the same instance. + * @throws ViatraQueryRuntimeException + */ + public IQueryResultProvider getResultProvider(PQuery query); + + /** + * Returns a result provider for a given query. Repeated calls may return the same instance. + * @param optional hints that may override engine and query defaults (as provided by {@link IQueryBackendHintProvider}). Can be null. + * @throws ViatraQueryRuntimeException + * @since 1.4 + */ + public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint hints); + + /** + * Returns an existing result provider for a given query, if it was previously constructed, returns null otherwise. + * Will not construct and initialize new result providers. + */ + public IQueryResultProvider peekExistingResultProvider(PQuery query); + + /** + * Propagates all pending updates in this query backend. The implementation of this method is optional, and it + * can be ignored entirely if the backend does not delay updates. + * @since 1.6 + */ + public void flushUpdates(); + + /** + * Disposes the query backend. + */ + public abstract void dispose(); + + /** + * @return the factory that created this backend, if this functionality is supported + * @since 2.1 + */ + public IQueryBackendFactory getFactory(); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java new file mode 100644 index 00000000..e264ab3f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactory.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * A Query Backend Factory identifies a query evaluator implementation, and can create an evaluator instance (an {@link IQueryBackend}) tied to a specific VIATRA Query engine upon request. + * + *

The factory is used as a lookup key for the backend instance, + * therefore implementors should either be singletons, or implement equals() / hashCode() accordingly. + * + * @author Bergmann Gabor + * + */ +public interface IQueryBackendFactory { + + /** + * Creates a new {@link IQueryBackend} instance tied to the given context elements. + * + * @return an instance of the class returned by {@link #getBackendClass()} that operates in the given context. + * @since 1.5 + */ + public IQueryBackend + create(IQueryBackendContext context); + + + /** + * The backend instances created by this factory are guaranteed to conform to the returned class. + */ + public Class getBackendClass(); + + /** + * Calculate the required capabilities, which are needed to execute the given pattern + * + * @since 1.4 + */ + public IMatcherCapability calculateRequiredCapability(PQuery query, QueryEvaluationHint hint); + + /** + * Returns whether the current backend is caching + * @since 2.0 + */ + public boolean isCaching(); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java new file mode 100644 index 00000000..8787814e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendFactoryProvider.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +/** + * A provider interface for {@link IQueryBackendFactory} instances. + * @since 2.0 + */ +public interface IQueryBackendFactoryProvider { + + /** + * Returns a query backend factory instance. The method should return the same instance in case of repeated calls. + */ + IQueryBackendFactory getFactory(); + + /** + * Returns whether the given query backend should be considered as system default. If multiple backends are + * registered as system default, it is undefined which one will be chosen. + */ + default boolean isSystemDefaultEngine() { + return false; + } + + /** + * Returns whether the given query backend should be considered as system default search backend. If multiple + * backends are registered as system default, it is undefined which one will be chosen. + */ + default boolean isSystemDefaultSearchBackend() { + return false; + } + + + /** + * Returns whether the given query backend should be considered as system default caching backend. If multiple + * backends are registered as system default, it is undefined which one will be chosen. + */ + default boolean isSystemDefaultCachingBackend() { + return false; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java new file mode 100644 index 00000000..9bb76349 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryBackendHintProvider.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * Provides query evaluation hints consisting of the Engine default hints and + * the hints provided by the pattern itself. + * + * @author Bergmann Gabor + * @since 0.9 + * @noimplement This interface is not intended to be implemented by clients, except in the tools.refinery.viatra.runtime plugin. + */ +public interface IQueryBackendHintProvider { + + /** + * Suggests query evaluation hints regarding a query. The returned hints reflects the default hints of the + * query engine merged with the hints provided by the pattern itself. These can be overridden via specific + * advanced API of the engine. + * + * @since 1.4 + */ + QueryEvaluationHint getQueryEvaluationHint(PQuery query); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java new file mode 100644 index 00000000..cd7d050f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IQueryResultProvider.java @@ -0,0 +1,202 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +import java.util.Optional; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Accuracy; + +/** + * An internal interface of the query backend that provides results of a given query. + * @author Bergmann Gabor + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IQueryResultProvider { + + /** + * Decides whether there are any matches of the pattern that conform to the given fixed values of some parameters. + * + * @param parameters + * array where each non-null element binds the corresponding pattern parameter to a fixed value. + * @pre size of input array must be equal to the number of parameters. + * @since 2.0 + */ + public boolean hasMatch(Object[] parameters); + + /** + * Decides whether there are any matches of the pattern that conform to the given fixed values of some parameters. + * + * @param parameterSeedMask + * a mask that extracts those parameters of the query (from the entire parameter list) that should be + * bound to a fixed value + * @param parameters + * the tuple of fixed values restricting the match set to be considered, in the same order as given in + * parameterSeedMask, so that for each considered match tuple, + * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold + * @since 2.0 + */ + public boolean hasMatch(TupleMask parameterSeedMask, ITuple projectedParameterSeed); + + /** + * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters. + * + * @param parameters + * array where each non-null element binds the corresponding pattern parameter to a fixed value. + * @pre size of input array must be equal to the number of parameters. + * @return the number of pattern matches found. + */ + public int countMatches(Object[] parameters); + + /** + * Returns the number of all matches of the pattern that conform to the given fixed values of some parameters. + * + * @param parameterSeedMask + * a mask that extracts those parameters of the query (from the entire parameter list) that should be + * bound to a fixed value + * @param parameters + * the tuple of fixed values restricting the match set to be considered, in the same order as given in + * parameterSeedMask, so that for each considered match tuple, + * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold + * @return the number of pattern matches found. + * @since 1.7 + */ + public int countMatches(TupleMask parameterSeedMask, ITuple projectedParameterSeed); + + /** + * Gives an estimate of the number of different groups the matches are projected into by the given mask + * (e.g. for an identity mask, this means the full match set size). The estimate must meet the required accuracy. + * + *

If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. + * In other words, query backends may deny an answer, or do their best to give an estimate without actually determining the match set of the query. + * However, caching backends are expected to simply return the indexed (projection) size, initialized on-demand if necessary. + * + *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. + * + * @return if available, an estimate of the cardinality of the projection of the match set, with the desired accuracy. + * + * @since 2.1 + */ + public Optional estimateCardinality(TupleMask groupMask, Accuracy requiredAccuracy); + + /** + * Gives an estimate of the average size of different groups the matches are projected into by the given mask + * (e.g. for an identity mask, this means 1, while for an empty mask, the result is match set size). + * The estimate must meet the required accuracy. + * + *

If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. + * In other words, query backends may deny an answer, or do their best to give an estimate without actually determining the match set of the query. + * However, caching backends are expected to simply return the exact value from the index, initialized on-demand if necessary. + * + *

For an empty match set, zero is acceptable as an exact answer. + * + *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. + * + * @return if available, an estimate of the average size of each projection group of the match set, with the desired accuracy. + * + * @since 2.1 + */ + public default Optional estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy) { + return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, this::estimateCardinality); + } + + /** + * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters. + * Neither determinism nor randomness of selection is guaranteed. + * + * @param parameters + * array where each non-null element binds the corresponding pattern parameter to a fixed value. + * @pre size of input array must be equal to the number of parameters. + * @return a match represented in the internal {@link Tuple} representation. + * @since 2.0 + */ + public Optional getOneArbitraryMatch(Object[] parameters); + + /** + * Returns an arbitrarily chosen match of the pattern that conforms to the given fixed values of some parameters. + * Neither determinism nor randomness of selection is guaranteed. + * + * @param parameterSeedMask + * a mask that extracts those parameters of the query (from the entire parameter list) that should be + * bound to a fixed value + * @param parameters + * the tuple of fixed values restricting the match set to be considered, in the same order as given in + * parameterSeedMask, so that for each considered match tuple, + * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold + * @return a match represented in the internal {@link Tuple} representation. + * @since 2.0 + */ + public Optional getOneArbitraryMatch(TupleMask parameterSeedMask, ITuple parameters); + + /** + * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters. + * + * @param parameters + * array where each non-null element binds the corresponding pattern parameter to a fixed value. + * @pre size of input array must be equal to the number of parameters. + * @return matches represented in the internal {@link Tuple} representation. + * @since 2.0 + */ + public Stream getAllMatches(Object[] parameters); + + /** + * Returns the set of all matches of the pattern that conform to the given fixed values of some parameters. + * + * @param parameterSeedMask + * a mask that extracts those parameters of the query (from the entire parameter list) that should be + * bound to a fixed value + * @param parameters + * the tuple of fixed values restricting the match set to be considered, in the same order as given in + * parameterSeedMask, so that for each considered match tuple, + * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold + * @return matches represented in the internal {@link Tuple} representation. + * @since 2.0 + */ + public Stream getAllMatches(TupleMask parameterSeedMask, ITuple parameters); + + /** + * The underlying query evaluator backend. + */ + public IQueryBackend getQueryBackend(); + + /** + * Internal method that registers low-level callbacks for match appearance and disappearance. + * + *

+ * Caution: This is a low-level callback that is invoked when the pattern matcher is not necessarily in a + * consistent state yet. Importantly, no model modification permitted during the callback. + * + *

+ * The callback can be unregistered via invoking {@link #removeUpdateListener(Object)} with the same tag. + * + * @param listener + * the listener that will be notified of each new match that appears or disappears, starting from now. + * @param listenerTag + * a tag by which to identify the listener for later removal by {@link #removeUpdateListener(Object)}. + * @param fireNow + * if true, the insertion update allback will be immediately invoked on all current matches as a one-time effect. + * + * @throws UnsupportedOperationException if this is a non-incremental backend + * (i.e. {@link IQueryBackend#isCaching()} on {@link #getQueryBackend()} returns false) + */ + public void addUpdateListener(final IUpdateable listener, final Object listenerTag, boolean fireNow); + + /** + * Removes an existing listener previously registered with the given tag. + * + * @throws UnsupportedOperationException if this is a non-incremental backend + * (i.e. {@link IQueryBackend#isCaching()} on {@link #getQueryBackend()} returns false) + */ + public void removeUpdateListener(final Object listenerTag); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java new file mode 100644 index 00000000..baf7144a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/IUpdateable.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * Internal interface for the query backend to singal an update to a query result. + * @author Bergmann Gabor + * @since 0.9 + * + */ +public interface IUpdateable { + + /** + * This callback method must be free of exceptions, even {@link RuntimeException}s (though not {@link Error}s). + * @param updateElement the tuple that is changed + * @param isInsertion true if the tuple appeared in the result set, false if disappeared from the result set + */ + public void update(Tuple updateElement, boolean isInsertion); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java new file mode 100644 index 00000000..eab92128 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryEvaluationHint.java @@ -0,0 +1,241 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +import java.util.AbstractMap; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * Provides VIATRA Query with additional hints on how a query should be evaluated. The same hint can be provided to multiple queries. + * + *

This class is immutable. Overriding options will create a new instance. + * + *

+ * Here be dragons: for advanced users only. + * + * @author Bergmann Gabor + * + */ +public class QueryEvaluationHint { + + /** + * @since 2.0 + * + */ + public enum BackendRequirement { + /** + * The current hint does not specify any backend requirement + */ + UNSPECIFIED, + /** + * The current hint specifies that the default search backend of the engine should be used + */ + DEFAULT_SEARCH, + /** + * The current hint specifies that the default caching backend of the engine should be used + */ + DEFAULT_CACHING, + /** + * The current hint specifies that a specific backend is to be used + */ + SPECIFIC + } + + final IQueryBackendFactory queryBackendFactory; + final Map, Object> backendHintSettings; + final BackendRequirement requirement; + + /** + * Specifies the suggested query backend requirements, and value settings for additional backend-specific options. + * + *

+ * The backend requirement type must not be {@link BackendRequirement#SPECIFIC} - for that case, use the constructor + * {@link #QueryEvaluationHint(Map, IQueryBackendFactory)}. + * + * @param backendHintSettings + * if non-null, each entry in the map overrides backend-specific options regarding query evaluation + * (null-valued map entries permitted to erase hints); passing null means default options associated with + * the query + * @param backendRequirementType + * defines the kind of backend requirement + * @since 2.0 + */ + public QueryEvaluationHint(Map, Object> backendHintSettings, BackendRequirement backendRequirementType) { + super(); + Preconditions.checkArgument(backendRequirementType != null, "Specific requirement needs to be set"); + Preconditions.checkArgument(backendRequirementType != BackendRequirement.SPECIFIC, "Specific backend requirement needs providing a corresponding backend type"); + this.queryBackendFactory = null; + this.requirement = backendRequirementType; + this.backendHintSettings = (backendHintSettings == null) + ? Collections., Object> emptyMap() + : new HashMap<>(backendHintSettings); + } + + /** + * Specifies the suggested query backend, and value settings for additional backend-specific options. The first + * parameter can be null; if the second parameter is null, it is expected that the other constructor is called + * instead with a {@link BackendRequirement#UNSPECIFIED} parameter. + * + * @param backendHintSettings + * if non-null, each entry in the map overrides backend-specific options regarding query evaluation + * (null-valued map entries permitted to erase hints); passing null means default options associated with + * the query + * @param queryBackendFactory + * overrides the query evaluator algorithm; passing null retains the default algorithm associated with + * the query + * @since 1.5 + */ + public QueryEvaluationHint( + Map, Object> backendHintSettings, + IQueryBackendFactory queryBackendFactory) { + super(); + this.queryBackendFactory = queryBackendFactory; + this.requirement = (queryBackendFactory == null) ? BackendRequirement.UNSPECIFIED : BackendRequirement.SPECIFIC; + this.backendHintSettings = (backendHintSettings == null) + ? Collections., Object> emptyMap() + : new HashMap<>(backendHintSettings); + } + + /** + * Returns the backend requirement described by this hint. If a specific backend is required, that can be queried by {@link #getQueryBackendFactory()}. + * @since 2.0 + */ + public BackendRequirement getQueryBackendRequirementType() { + return requirement; + } + + /** + * A suggestion for choosing the query evaluator algorithm. + * + *

+ * Returns null iff {@link #getQueryBackendRequirementType()} does not return {@link BackendRequirement#SPECIFIC}; + * in such cases a corresponding default backend is selected inside the engine + */ + public IQueryBackendFactory getQueryBackendFactory() { + return queryBackendFactory; + } + + /** + * Each entry in the immutable map overrides backend-specific options regarding query evaluation. + * + *

The map is non-null, even if empty. + * Null-valued map entries are also permitted to erase hints via {@link #overrideBy(QueryEvaluationHint)}. + * + * @since 1.5 + */ + public Map, Object> getBackendHintSettings() { + return backendHintSettings; + } + + + /** + * Override values in this hint and return a consolidated instance. + * + * @since 1.4 + */ + public QueryEvaluationHint overrideBy(QueryEvaluationHint overridingHint){ + if (overridingHint == null) + return this; + + BackendRequirement overriddenRequirement = this.getQueryBackendRequirementType(); + if (overridingHint.getQueryBackendRequirementType() != BackendRequirement.UNSPECIFIED) { + overriddenRequirement = overridingHint.getQueryBackendRequirementType(); + } + Map, Object> hints = new HashMap<>(this.getBackendHintSettings()); + if (overridingHint.getBackendHintSettings() != null) { + hints.putAll(overridingHint.getBackendHintSettings()); + } + if (overriddenRequirement == BackendRequirement.SPECIFIC) { + IQueryBackendFactory factory = this.getQueryBackendFactory(); + if (overridingHint.getQueryBackendFactory() != null) { + factory = overridingHint.getQueryBackendFactory(); + } + return new QueryEvaluationHint(hints, factory); + } else { + return new QueryEvaluationHint(hints, overriddenRequirement); + } + } + + /** + * Returns whether the given hint option is overridden. + * @since 1.5 + */ + public boolean isOptionOverridden(QueryHintOption option) { + return getBackendHintSettings().containsKey(option); + } + + /** + * Returns the value of the given hint option from the given hint collection, or null if not defined. + * @since 1.5 + */ + @SuppressWarnings("unchecked") + public HintValue getValueOrNull(QueryHintOption option) { + return (HintValue) getBackendHintSettings().get(option); + } + + /** + * Returns the value of the given hint option from the given hint collection, or the default value if not defined. + * Intended to be called by backends to find out the definitive value that should be considered. + * @since 1.5 + */ + public HintValue getValueOrDefault(QueryHintOption option) { + return option.getValueOrDefault(this); + } + + @Override + public int hashCode() { + return Objects.hash(backendHintSettings, queryBackendFactory, requirement); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + QueryEvaluationHint other = (QueryEvaluationHint) obj; + return Objects.equals(backendHintSettings, other.backendHintSettings) + && + Objects.equals(queryBackendFactory, other.queryBackendFactory) + && + Objects.equals(requirement, other.requirement) + ; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + + if (getQueryBackendFactory() != null) + sb.append("backend: ").append(getQueryBackendFactory().getBackendClass().getSimpleName()); + if (! backendHintSettings.isEmpty()) { + sb.append("hints: "); + if(backendHintSettings instanceof AbstractMap){ + sb.append(backendHintSettings.toString()); + } else { + // we have to iterate on the contents + + String joinedHintMap = backendHintSettings.entrySet().stream() + .map(setting -> setting.getKey() + "=" + setting.getValue()).collect(Collectors.joining(", ")); + sb.append('{').append(joinedHintMap).append('}'); + } + } + + final String result = sb.toString(); + return result.isEmpty() ? "defaults" : result; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java new file mode 100644 index 00000000..2c6bb4de --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/QueryHintOption.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +import java.util.Map; +import java.util.Objects; + +/** + * Each instance of this class corresponds to a given hint option. + * + * It is recommended to expose options to clients (and query backends) as public static fields. + * + * @author Gabor Bergmann + * @since 1.5 + */ +public class QueryHintOption { + + private String optionQualifiedName; + private HintValue defaultValue; + + /** + * Instantiates an option object with the given name and default value. + */ + public QueryHintOption(String optionQualifiedName, HintValue defaultValue) { + this.optionQualifiedName = optionQualifiedName; + this.defaultValue = defaultValue; + } + + /** + * This is the recommended constructor for hint options defined as static fields within an enclosing class. + * Combines the qualified name of the hint from the (qualified) name of the enclosing class and a local name (unique within that class). + */ + public QueryHintOption(Class optionNamespace, String optionLocalName, T defaultValue) { + this(String.format("%s@%s", optionLocalName, optionNamespace.getName()), defaultValue); + } + + /** + * Returns the qualified name, a unique string identifier of the option. + */ + public String getQualifiedName() { + return optionQualifiedName; + } + + /** + * Returns the default value of this hint option, which is to be used by query backends in the case no overriding value is assigned. + */ + public HintValue getDefaultValue() { + return defaultValue; + } + + /** + * Returns the value of this hint option from the given hint collection, or the default value if not defined. + * Intended to be called by backends to find out the definitive value that should be considered. + */ + @SuppressWarnings("unchecked") + public HintValue getValueOrDefault(QueryEvaluationHint hints) { + Object value = hints.getValueOrNull(this); + if (value == null) + return getDefaultValue(); + else { + return (HintValue) value; + } + } + + + /** + * Returns the value of this hint option from the given hint collection, or null if not defined. + */ + public HintValue getValueOrNull(QueryEvaluationHint hints) { + return hints.getValueOrNull(this); + } + + /** + * Returns whether this hint option is defined in the given hint collection. + */ + public boolean isOverriddenIn(QueryEvaluationHint hints) { + return hints.isOptionOverridden(this); + } + + /** + * Puts a value of this hint option into an option-to-value map. + * + *

This method is offered in lieu of a builder API. + * Use this method on any number of hint options in order to populate an option-value map. + * Then instantiate the immutable {@link QueryEvaluationHint} using the map. + * + * @see #insertValueIfNondefault(Map, Object) + * @return the hint value that was previously present in the map under this hint option, carrying over the semantics of {@link Map#put(Object, Object)}. + */ + @SuppressWarnings("unchecked") + public HintValue insertOverridingValue(Map, Object> hints, HintValue overridingValue) { + return (HintValue) hints.put(this, overridingValue); + } + + /** + * Puts a value of this hint option into an option-to-value map, if the given value differs from the default value of the option. + * If the default value is provided instead, then the map is not updated. + * + *

This method is offered in lieu of a builder API. + * Use this method on any number of hint options in order to populate an option-value map. + * Then instantiate the immutable {@link QueryEvaluationHint} using the map. + * + * @see #insertOverridingValue(Map, Object) + * @since 2.0 + */ + public void insertValueIfNondefault(Map, Object> hints, HintValue overridingValue) { + if (!Objects.equals(defaultValue, overridingValue)) + hints.put(this, overridingValue); + } + + @Override + public String toString() { + return optionQualifiedName; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java new file mode 100644 index 00000000..6ec6d53e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/backend/ResultProviderRequestor.java @@ -0,0 +1,74 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.backend; + +import tools.refinery.viatra.runtime.matchers.context.IQueryResultProviderAccess; +import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; + +/** + * Uniform way of requesting result providers for pattern calls within queries. + * Intended users are query backends, for calling other backends to deliver results of dependee queries. + * + * @author Gabor Bergmann + * @since 2.1 + */ +public class ResultProviderRequestor { + IQueryBackend callerBackend; + IQueryResultProviderAccess resultProviderAccess; + IQueryBackendHintProvider hintProvider; + ICallDelegationStrategy delegationStrategy; + QueryEvaluationHint callerHint; + QueryEvaluationHint universalOverride; + + + /** + * @param callerBackend the actual backend evaluating the calling pattern. + * @param resultProviderAccess + * @param hintProvider + * @param delegationStrategy + * @param callerHint a hint under which the calling pattern is evaluated, + * @param universalOverride if non-null, overrides the hint with extra options after the {@link ICallDelegationStrategy} + */ + public ResultProviderRequestor(IQueryBackend callerBackend, IQueryResultProviderAccess resultProviderAccess, + IQueryBackendHintProvider hintProvider, ICallDelegationStrategy delegationStrategy, + QueryEvaluationHint callerHint, QueryEvaluationHint universalOverride) { + super(); + this.callerBackend = callerBackend; + this.resultProviderAccess = resultProviderAccess; + this.hintProvider = hintProvider; + this.delegationStrategy = delegationStrategy; + this.callerHint = callerHint; + this.universalOverride = universalOverride; + } + + + + + /** + * + * @param call a {@link PConstraint} in a query that calls another query. + * @param spotOverride if non-null, overrides the hint with extra options after the {@link ICallDelegationStrategy} + * and the universal override specified in the constructor + * @return the obtained result provider + */ + public IQueryResultProvider requestResultProvider(IQueryReference call, QueryEvaluationHint spotOverride) { + QueryEvaluationHint hints = + delegationStrategy.transformHints(call, callerHint, callerBackend, hintProvider); + + if (universalOverride != null) + hints = hints.overrideBy(universalOverride); + + if (spotOverride != null) + hints = hints.overrideBy(spotOverride); + + return resultProviderAccess.getResultProvider(call.getReferredQuery(), hints); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java new file mode 100644 index 00000000..99611758 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryMetaContext.java @@ -0,0 +1,69 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * Common abstract class for implementers of {@link IQueryMetaContext} + * + * @author Grill Balázs + * @since 1.3 + * + */ +public abstract class AbstractQueryMetaContext implements IQueryMetaContext { + + /** + * @since 2.0 + */ + @Override + public Map> getConditionalImplications(IInputKey implyingKey) { + return new HashMap<>(); + } + + /** + * @since 1.6 + */ + @Override + public boolean canLeadOutOfScope(IInputKey key) { + return key.getArity() > 1; + } + + /** + * @since 1.6 + */ + @Override + public Comparator getSuggestedEliminationOrdering() { + return (o1, o2) -> 0; + } + + /** + * @since 1.6 + */ + @Override + public Collection getWeakenedAlternatives(IInputKey implyingKey) { + return Collections.emptySet(); + } + + @Override + public boolean isPosetKey(IInputKey key) { + return false; + } + + @Override + public IPosetComparator getPosetComparator(Iterable key) { + return null; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java new file mode 100644 index 00000000..c797eff9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/AbstractQueryRuntimeContext.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +/** + * This class is intended to be extended by implementors. The main purpose of this abstract implementation to protect + * implementors from future changes in the interface. + * + * @author Grill Balázs + * @since 1.4 + * + */ +public abstract class AbstractQueryRuntimeContext implements IQueryRuntimeContext { + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java new file mode 100644 index 00000000..4dbcca88 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IInputKey.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +/** + * An input key identifies an input (extensional) relation, such as the instance set of a given node or edge type, or the direct containment relation. + * + *

The input key, at the very minimum, is associated with an arity (number of columns), a user-friendly name, and a string identifier (for distributive purposes). + * + *

The input key itself must be an immutable data object that properly overrides equals() and hashCode(). + * It must be instantiable without using the query context object, so that query specifications may construct the appropriate PQueries. + * + * @author Bergmann Gabor + * + */ +public interface IInputKey { + + /** + * A user-friendly name that can be shown on screen for debug purposes, included in exceptions, etc. + */ + public String getPrettyPrintableName(); + /** + * An internal string identifier that can be used to uniquely identify to input key (relevant for distributed applications). + */ + public String getStringID(); + + /** + * The width of tuples in this relation. + */ + public int getArity(); + + /** + * Returns true iff instance tuples of the key can be enumerated. + *

If false, the runtime can only test tuple membership in the extensional relation identified by the key, but not enumerate member tuples in general. + */ + boolean isEnumerable(); + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java new file mode 100644 index 00000000..e2d5bcee --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IPosetComparator.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * Implementations of this interface aid the query engine with the ordering of poset elements. This information is + * particularly important in the delete and re-derive evaluation mode because they let the engine identify monotone + * change pairs. + * + * @author Tamas Szabo + * @since 1.6 + */ +public interface IPosetComparator { + + /** + * Returns true if the 'left' tuple of poset elements is smaller or equal than the 'right' tuple of poset elements according to + * the partial order that this poset comparator employs. + * + * @param left + * the left tuple of poset elements + * @param right + * the right tuple of poset elements + * @return true if left is smaller or equal to right, false otherwise + */ + public boolean isLessOrEqual(final Tuple left, final Tuple right); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java new file mode 100644 index 00000000..04f00aaa --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryBackendContext.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +import org.apache.log4j.Logger; +import tools.refinery.viatra.runtime.matchers.backend.IMatcherCapability; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * This interface is a collector which holds every API that is provided by the engine to control + * the operation of the backends. + * + * @since 1.5 + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IQueryBackendContext { + + Logger getLogger(); + + IQueryRuntimeContext getRuntimeContext(); + + IQueryCacheContext getQueryCacheContext(); + + IQueryBackendHintProvider getHintProvider(); + + IQueryResultProviderAccess getResultProviderAccess(); + + QueryAnalyzer getQueryAnalyzer(); + + /** + * @since 2.0 + */ + IMatcherCapability getRequiredMatcherCapability(PQuery query, QueryEvaluationHint overrideHints); + + /** + * @since 1.6 + */ + boolean areUpdatesDelayed(); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java new file mode 100644 index 00000000..617207f6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryCacheContext.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; +import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * Provides information on already cached queries to query evaluator backends at runtime. + * + * @author Bergmann Gabor + * + */ +public interface IQueryCacheContext { + + /** + * Checks if there already is a caching result provider for the given query. + *

Returns false if called while the caching result provider of the given query is being constructed in the first place. + */ + public boolean isResultCached(PQuery query); + + /** + * Returns a caching result provider for the given query; it must be constructed if it does not exist yet. + *

Caution: behavior undefined if called while the caching result provider of the given query is being constructed. + * Beware of infinite loops. + *

Postcondition: {@link IQueryBackend#isCaching()} returns true for the {@link #getQueryBackend()} of the returned provider + * + * @throws ViatraQueryRuntimeException + */ + public IQueryResultProvider getCachingResultProvider(PQuery query); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java new file mode 100644 index 00000000..4c22a3cb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryMetaContext.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +import java.util.Collection; +import java.util.Comparator; +import java.util.Map; +import java.util.Set; + +/** + * Provides metamodel information (relationship of input keys) to query evaluator backends at runtime and at query planning time. + * + * @noimplement Implementors should extend {@link AbstractQueryMetaContext} instead of directly implementing this interface. + * @author Bergmann Gabor + */ +public interface IQueryMetaContext { + + /** + * Returns true iff instance tuples of the given key can be enumerated. + *

If false, the runtime can only test tuple membership in the extensional relation identified by the key, but not enumerate member tuples in general. + *

Equivalent to {@link IInputKey#isEnumerable()}. + */ + boolean isEnumerable(IInputKey key); + + /** + * Returns true iff the set of instance tuples of the given key is immutable. + *

If false, the runtime provides notifications upon change. + */ + boolean isStateless(IInputKey key); + + /** + * Returns a set of implications (weakened alternatives), + * with a suggestion for the query planner that satisfying them first may help in satisfying the implying key. + *

Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys. + *

Must follow directly or transitively from implications of {@link #getImplications(IInputKey)}. + * @since 1.6 + */ + Collection getWeakenedAlternatives(IInputKey implyingKey); + + /** + * Returns known direct implications, e.g. edge supertypes, edge opposites, node type constraints, etc. + *

Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys. + */ + Collection getImplications(IInputKey implyingKey); + + /** + * Returns known "double dispatch" implications, where the given implying key implies other input keys under certain additional conditions (themselves input keys). + * For example, a "type x, unscoped" input key may imply the "type x, in scope" input key under the condition of the input key "x is in scope" + * + *

Note that for the obvious reasons, enumerable keys can only be implied by enumerable keys (either as the implying key or as the additional condition). + *

Note that symmetry is not required, i.e. the additional conditions do not have to list the same conditional implication. + * @return multi-map, where the keys are additional conditions and the values are input key implications jointly implied by the condition and the given implying key. + * @since 2.0 + */ + Map> getConditionalImplications(IInputKey implyingKey); + + /** + * Returns functional dependencies of the input key expressed in terms of column indices. + * + *

Each entry of the map is a functional dependency rule, where the entry key specifies source columns and the entry value specifies target columns. + */ + Map, Set> getFunctionalDependencies(IInputKey key); + + /** + * For query normalizing, this is the order suggested for trying to eliminate input keys. + * @since 1.6 + */ + Comparator getSuggestedEliminationOrdering(); + + /** + * Tells whether the given {@link IInputKey} is an edge and may lead out of scope. + * + * @since 1.6 + */ + boolean canLeadOutOfScope(IInputKey key); + + /** + * Returns true if the given {@link IInputKey} represents a poset type. + * @since 1.6 + */ + boolean isPosetKey(IInputKey key); + + /** + * Returns an {@link IPosetComparator} for the given set of {@link IInputKey}s. + * + * @param keys an iterable collection of input keys + * @return the poset comparator + * @since 1.6 + */ + IPosetComparator getPosetComparator(Iterable keys); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java new file mode 100644 index 00000000..7fecd01a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryResultProviderAccess.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +import tools.refinery.viatra.runtime.matchers.backend.IQueryResultProvider; +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * This interface exposes API to request {@link IQueryResultProvider} for {@link PQuery} instances. + * + * @author Grill Balázs + * @since 1.5 + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IQueryResultProviderAccess { + + /** + * Get a result provider for the given {@link PQuery}, which conforms the capabilities requested by the + * given {@link QueryEvaluationHint} object. + * @throws ViatraQueryRuntimeException + */ + public IQueryResultProvider getResultProvider(PQuery query, QueryEvaluationHint overrideHints); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java new file mode 100644 index 00000000..c2e90614 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java @@ -0,0 +1,281 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +import java.lang.reflect.InvocationTargetException; +import java.util.Optional; +import java.util.concurrent.Callable; + +import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Accuracy; + +/** + * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime. + * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface. + * + * @author Bergmann Gabor + * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead. + */ +public interface IQueryRuntimeContext { + /** + * Provides metamodel-specific info independent of the runtime instance model. + */ + public IQueryMetaContext getMetaContext(); + + + /** + * The given callable will be executed, and all model traversals will be delayed until the execution is done. If + * there are any outstanding information to be read from the model, a single coalesced model traversal will + * initialize the caches and deliver the notifications. + * + *

Calls may be nested. A single coalesced traversal will happen at the end of the outermost call. + * + *

Caution: results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. + * For example, if a certain input key is not cached yet, an empty relation may be reported during callable.call(); the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. + * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}). + * + * @param callable + */ + public abstract V coalesceTraversals(Callable callable) throws InvocationTargetException; + /** + * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}). + */ + public boolean isCoalescing(); + + /** + * Returns true if index is available for the given key providing the given service. + * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @since 1.4 + */ + public boolean isIndexed(IInputKey key, IndexingService service); + + /** + * If the given (enumerable) input key is not yet indexed, the model will be traversed + * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) + * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging + * multiple indexing requests to an appropriate level. + * + *

Postcondition: After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key + * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false. + * + *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @since 1.4 + */ + public void ensureIndexed(IInputKey key, IndexingService service); + + /** + * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple. + * + * @param key an input key + * @param seedMask + * a mask that extracts those parameters of the input key (from the entire parameter list) that should be + * bound to a fixed value; must not be null. Note: any given index must occur at most once in seedMask. + * @param seed + * the tuple of fixed values restricting the match set to be considered, in the same order as given in + * parameterSeedMask, so that for each considered match tuple, + * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. + * + * @return the number of tuples in the model for the given key and seed + * + *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @since 1.7 + */ + public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed); + + + /** + * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask + * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy. + * + *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. + * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. + * + *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. + * + * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy. + * + * @since 2.1 + */ + public Optional estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy); + + + /** + * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask + * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). + * The estimate must meet the required accuracy. + * + *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. + * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. + * + *

For an empty relation, zero is acceptable as an exact answer. + * + *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. + * + * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy. + * + * @since 2.1 + */ + public default Optional estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { + if (key.isEnumerable()) { + return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, + (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy)); + } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty(); + } + + + /** + * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple. + * + * @param key an input key + * @param seedMask + * a mask that extracts those parameters of the input key (from the entire parameter list) that should be + * bound to a fixed value; must not be null. Note: any given index must occur at most once in seedMask. + * @param seed + * the tuple of fixed values restricting the match set to be considered, in the same order as given in + * parameterSeedMask, so that for each considered match tuple, + * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. + * @return the tuples in the model for the given key and seed + * + *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @since 1.7 + */ + public Iterable enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed); + + /** + * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples + * are bound by the seed except for one. + * + *

+ * Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given + * tuple, and then returns the single value from each tuple which is not bound by the ssed mask. + * + * @param key + * an input key + * @param seedMask + * a mask that extracts those parameters of the input key (from the entire parameter list) that should be + * bound to a fixed value; must not be null. Note: any given index must occur at most + * once in seedMask, and seedMask must include all parameters in any arbitrary order except one. + * @param seed + * the tuple of fixed values restricting the match set to be considered, in the same order as given in + * parameterSeedMask, so that for each considered match tuple, + * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. + * @return the objects in the model for the given key and seed + * + *

+ * Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @throws IllegalArgumentException + * if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @since 1.7 + */ + public Iterable enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed); + + /** + * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples + * are bound by the seed. + * + *

+ * Returns whether the given tuple is in the extensional relation identified by the input key. + * + *

+ * Note: this call works for non-enumerable input keys as well. + * + * @param key + * an input key + * @param seed + * the tuple of fixed values restricting the match set to be considered, in the same order as given in + * parameterSeedMask, so that for each considered match tuple, + * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. + * @return true iff there is at least a single tuple contained in the relation that corresponds to the seed tuple + * @since 2.0 + */ + public boolean containsTuple(IInputKey key, ITuple seed); + + + /** + * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple. + *

This should be called after invoking + * + * @param key an input key + * @param seed can be null or a tuple with matching arity; + * if non-null, only those updates in the model are notified about + * that match the seed at positions where the seed is non-null. + * @param listener will be notified of future changes + * + *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + */ + public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); + + /** + * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple. + * + * @param key an input key + * @param seed can be null or a tuple with matching arity; + * if non-null, only those updates in the model are notified about + * that match the seed at positions where the seed is non-null. + * @param listener will no longer be notified of future changes + * + *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. + */ + public void removeUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); + /* + TODO: uniqueness + */ + + /** + * Wraps the external element into the internal representation that is to be used by the query backend + *

model element -> internal object. + *

null must be mapped to null. + */ + public Object wrapElement(Object externalElement); + + /** + * Unwraps the internal representation of the element into its original form + *

internal object -> model element + *

null must be mapped to null. + */ + public Object unwrapElement(Object internalElement); + + /** + * Unwraps the tuple of elements into the internal representation that is to be used by the query backend + *

model elements -> internal objects + *

null must be mapped to null. + */ + public Tuple wrapTuple(Tuple externalElements); + + /** + * Unwraps the tuple of internal representations of elements into their original forms + *

internal objects -> model elements + *

null must be mapped to null. + */ + public Tuple unwrapTuple(Tuple internalElements); + + /** + * Starts wildcard indexing for the given service. After this call, no registration is required for this {@link IndexingService}. + * a previously set wildcard level cannot be lowered, only extended. + * @since 1.4 + */ + public void ensureWildcardIndexing(IndexingService service); + + /** + * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as + * the indexing is finished. The callback is executed only once, then is removed from the callback queue. + * @param traversalCallback + * @throws InvocationTargetException + * @since 1.4 + */ + public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException; +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java new file mode 100644 index 00000000..7be27d56 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContextListener.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * Listens for changes in the runtime context. + * @author Bergmann Gabor + * + */ +public interface IQueryRuntimeContextListener { + + /** + * The given tuple was inserted into or removed from the input relation indicated by the given key. + * @param key the key identifying the input relation that was updated + * @param updateTuple the tuple that was inserted or removed + * @param isInsertion true if it was an insertion, false otherwise. + */ + public void update(IInputKey key, Tuple updateTuple, boolean isInsertion); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java new file mode 100644 index 00000000..8210765d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IndexingService.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +/** + * These are the different services which can be provided by an {@link IQueryRuntimeContext} implementation. + * + * @author Grill Balázs + * @since 1.4 + * + */ +public enum IndexingService { + + /** + * Cardinality information is available. Makes possible to calculate + * unseeded calls of {@link IQueryRuntimeContext#countTuples(IInputKey, tools.refinery.viatra.runtime.matchers.tuple.Tuple)} + */ + STATISTICS, + + /** + * The indexer can provide notifications about changes in the model. + */ + NOTIFICATIONS, + + /** + * Enables enumeration of instances and reverse-navigation. + */ + INSTANCES + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java new file mode 100644 index 00000000..2a403810 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/InputKeyImplication.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Data object representing the implication of an input key, in use cases including edge supertypes, edge opposites, node type constraints, etc. + * + *

Each instance tuple of the implying input key (if given) implies the presence of an instance tuple of the implied input key consisting of elements of the original tuple at given positions. + * When the input key is null, it is not an input constraint but some other source that implies input keys. + * + *

The implication is an immutable data object. + * + * @author Bergmann Gabor + * + */ +public final class InputKeyImplication { + private IInputKey implyingKey; + private IInputKey impliedKey; + private List impliedIndices; + + /** + * Optional. Instance tuples of this input key imply an instance tuple of another key. + * Sometimes it is not an input key that implies other input keys, so this attribute can be null. + */ + public IInputKey getImplyingKey() { + return implyingKey; + } + /** + * An instance tuple of this input key is implied by another key. + */ + public IInputKey getImpliedKey() { + return impliedKey; + } + /** + * The implied instance tuple consists of the values in the implying tuple at these indices. + */ + public List getImpliedIndices() { + return impliedIndices; + } + /** + * @param implyingKey instance tuples of this input key imply an instance tuple of the other key. + * @param impliedKey instance tuple of this input key is implied by the other key. + * @param implyingIndices the implied instance tuple consists of the values in the implying tuple at these indices. + */ + public InputKeyImplication(IInputKey implyingKey, IInputKey impliedKey, + List implyingIndices) { + super(); + this.implyingKey = implyingKey; + this.impliedKey = impliedKey; + this.impliedIndices = Collections.unmodifiableList(new ArrayList<>(implyingIndices)); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((impliedIndices == null) ? 0 : impliedIndices.hashCode()); + result = prime * result + + ((impliedKey == null) ? 0 : impliedKey.hashCode()); + result = prime * result + + ((implyingKey == null) ? 0 : implyingKey.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof InputKeyImplication)) + return false; + InputKeyImplication other = (InputKeyImplication) obj; + if (impliedIndices == null) { + if (other.impliedIndices != null) + return false; + } else if (!impliedIndices.equals(other.impliedIndices)) + return false; + if (impliedKey == null) { + if (other.impliedKey != null) + return false; + } else if (!impliedKey.equals(other.impliedKey)) + return false; + if (implyingKey == null) { + if (other.implyingKey != null) + return false; + } else if (!implyingKey.equals(other.implyingKey)) + return false; + return true; + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java new file mode 100644 index 00000000..f9b05e5b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/BaseInputKeyWrapper.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context.common; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; + + +/** + * An input key that is identified by a single wrapped object and the class of the wrapper. + * @author Bergmann Gabor + * + */ +public abstract class BaseInputKeyWrapper implements IInputKey { + protected Wrapped wrappedKey; + + public BaseInputKeyWrapper(Wrapped wrappedKey) { + super(); + this.wrappedKey = wrappedKey; + } + + public Wrapped getWrappedKey() { + return wrappedKey; + } + + + @Override + public int hashCode() { + return ((wrappedKey == null) ? 0 : wrappedKey.hashCode()); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(this.getClass().equals(obj.getClass()))) + return false; + BaseInputKeyWrapper other = (BaseInputKeyWrapper) obj; + if (wrappedKey == null) { + if (other.wrappedKey != null) + return false; + } else if (!wrappedKey.equals(other.wrappedKey)) + return false; + return true; + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java new file mode 100644 index 00000000..eb972c2d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/common/JavaTransitiveInstancesKey.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context.common; + + + +/** + * Instance tuples are of form (x), where object x is an instance of the given Java class or its subclasses. + *

Fine print 1: classes with the same name are considered equivalent. + * Can be instantiated with class name, even if the class itself is not loaded yet; but if the class is available, passing it in the constructor is beneficial to avoid classloading troubles. + *

Fine print 2: primitive types (char, etc.) are transparently treated as their wrapper class (Character, etc.). + *

Non-enumerable type, can only be checked. + *

Stateless type (objects can't change their type) + * @author Bergmann Gabor + * +*/ +public class JavaTransitiveInstancesKey extends BaseInputKeyWrapper { + + /** + * The actual Class whose (transitive) instances this relation contains. Can be null at compile time, if only the name is available. + * Can be a primitive. + */ + private Class cachedOriginalInstanceClass; + + /** + * Same as {@link #cachedOriginalInstanceClass}, but primitive classes are replaced with their wrapper classes (e.g. int --> java.lang.Integer). + */ + private Class cachedWrapperInstanceClass; + + /** + * Preferred constructor. + */ + public JavaTransitiveInstancesKey(Class instanceClass) { + this(primitiveTypeToWrapperClass(instanceClass).getName()); + this.cachedOriginalInstanceClass = instanceClass; + } + + /** + * Call this constructor only in contexts where the class itself is not available for loading, e.g. it has not yet been compiled. + */ + public JavaTransitiveInstancesKey(String className) { + super(className); + } + + + /** + * Returns null if class cannot be loaded. + */ + private Class getOriginalInstanceClass() { + if (cachedOriginalInstanceClass == null) { + try { + resolveClassInternal(); + } catch (ClassNotFoundException e) { + // class not yet available at this point + } + } + return cachedOriginalInstanceClass; + } + + + /** + * @return non-null instance class + * @throws ClassNotFoundException + */ + private Class forceGetOriginalInstanceClass() throws ClassNotFoundException { + if (cachedOriginalInstanceClass == null) { + resolveClassInternal(); + } + return cachedOriginalInstanceClass; + } + + /** + * @return non-null instance class, wrapped if primitive class + * @throws ClassNotFoundException + */ + public Class forceGetWrapperInstanceClass() throws ClassNotFoundException { + forceGetOriginalInstanceClass(); + return getWrapperInstanceClass(); + } + /** + * @return non-null instance class, wrapped if primitive class + * @throws ClassNotFoundException + */ + public Class forceGetInstanceClass() throws ClassNotFoundException { + return forceGetWrapperInstanceClass(); + } + + /** + * @return instance class, wrapped if primitive class, null if class cannot be loaded + */ + public Class getWrapperInstanceClass() { + if (cachedWrapperInstanceClass == null) { + cachedWrapperInstanceClass = primitiveTypeToWrapperClass(getOriginalInstanceClass()); + } + return cachedWrapperInstanceClass; + } + /** + * @return instance class, wrapped if primitive class, null if class cannot be loaded + */ + public Class getInstanceClass() { + return getWrapperInstanceClass(); + } + + private void resolveClassInternal() throws ClassNotFoundException { + cachedOriginalInstanceClass = Class.forName(wrappedKey); + } + + @Override + public String getPrettyPrintableName() { + getWrapperInstanceClass(); + return cachedWrapperInstanceClass == null ? wrappedKey == null ? "" : wrappedKey : cachedWrapperInstanceClass.getName(); + } + + @Override + public String getStringID() { + return "javaClass#"+ getPrettyPrintableName(); + } + + @Override + public int getArity() { + return 1; + } + + @Override + public boolean isEnumerable() { + return false; + } + + @Override + public String toString() { + return this.getPrettyPrintableName(); + } + + private static Class primitiveTypeToWrapperClass(Class instanceClass) { + if (instanceClass != null && instanceClass.isPrimitive()) { + if (Void.TYPE.equals(instanceClass)) + return Void.class; + if (Boolean.TYPE.equals(instanceClass)) + return Boolean.class; + if (Character.TYPE.equals(instanceClass)) + return Character.class; + if (Byte.TYPE.equals(instanceClass)) + return Byte.class; + if (Short.TYPE.equals(instanceClass)) + return Short.class; + if (Integer.TYPE.equals(instanceClass)) + return Integer.class; + if (Long.TYPE.equals(instanceClass)) + return Long.class; + if (Float.TYPE.equals(instanceClass)) + return Float.class; + if (Double.TYPE.equals(instanceClass)) + return Double.class; + } + return instanceClass; + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java new file mode 100644 index 00000000..bb24ec8c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/surrogate/SurrogateQueryRegistry.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Abel Hegedus, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.context.surrogate; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.util.IProvider; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; +import tools.refinery.viatra.runtime.matchers.util.SingletonInstanceProvider; + +/** + * @author Abel Hegedus + * + */ +public class SurrogateQueryRegistry { + + private Map> registeredSurrogateQueryMap = new HashMap<>(); + private Map> dynamicSurrogateQueryMap = new HashMap<>(); + + /** + * Hidden constructor + */ + private SurrogateQueryRegistry() { + } + + private static final SurrogateQueryRegistry INSTANCE = new SurrogateQueryRegistry(); + + public static SurrogateQueryRegistry instance() { + return INSTANCE; + } + + /** + * + * @param feature + * @param surrogateQuery + * @return the previous surrogate query associated with feature, or null if there was no such query FQN registered + * @throws IllegalArgumentException if feature or surrogateQuery is null + */ + public IProvider registerSurrogateQueryForFeature(IInputKey feature, PQuery surrogateQuery) { + Preconditions.checkArgument(surrogateQuery != null, "Surrogate query must not be null!"); + return registerSurrogateQueryForFeature(feature, new SingletonInstanceProvider(surrogateQuery)); + } + + /** + * + * @param feature + * @param surrogateQuery + * @return the previous surrogate query associated with feature, or null + * if there was no such query registered + * @throws IllegalArgumentException + * if feature or surrogateQuery is null + */ + public IProvider registerSurrogateQueryForFeature(IInputKey feature, IProvider surrogateQueryProvider) { + Preconditions.checkArgument(feature != null, "Feature must not be null!"); + Preconditions.checkArgument(surrogateQueryProvider != null, "Surrogate query must not be null!"); + return registeredSurrogateQueryMap.put(feature, surrogateQueryProvider); + } + + public IProvider addDynamicSurrogateQueryForFeature(IInputKey feature, PQuery surrogateQuery) { + Preconditions.checkArgument(surrogateQuery != null, "Surrogate query FQN must not be null!"); + return addDynamicSurrogateQueryForFeature(feature, new SingletonInstanceProvider(surrogateQuery)); + } + + public IProvider addDynamicSurrogateQueryForFeature(IInputKey feature, IProvider surrogateQuery) { + Preconditions.checkArgument(feature != null, "Feature must not be null!"); + Preconditions.checkArgument(surrogateQuery != null, "Surrogate query FQN must not be null!"); + return dynamicSurrogateQueryMap.put(feature, surrogateQuery); + } + + public IProvider removeDynamicSurrogateQueryForFeature(IInputKey feature) { + Preconditions.checkArgument(feature != null, "Feature must not be null!"); + return dynamicSurrogateQueryMap.remove(feature); + } + + /** + * + * @param feature that may have surrogate query defined, null not allowed + * @return true if the feature has a surrogate query defined + * @throws IllegalArgumentException if feature is null + */ + public boolean hasSurrogateQueryFQN(IInputKey feature) { + Preconditions.checkArgument(feature != null, "Feature must not be null!"); + boolean surrogateExists = dynamicSurrogateQueryMap.containsKey(feature); + if(!surrogateExists){ + surrogateExists = registeredSurrogateQueryMap.containsKey(feature); + } + return surrogateExists; + } + + /** + * + * @param feature for which the surrogate query FQN should be returned + * @return the surrogate query FQN defined for the feature + * @throws IllegalArgumentException if feature is null + * @throws NoSuchElementException if the feature has no surrogate query defined, use {@link #hasSurrogateQueryFQN} to check + */ + public PQuery getSurrogateQuery(IInputKey feature) { + Preconditions.checkArgument(feature != null, "Feature must not be null!"); + IProvider surrogate = dynamicSurrogateQueryMap.get(feature); + if(surrogate == null) { + surrogate = registeredSurrogateQueryMap.get(feature); + } + if(surrogate != null) { + return surrogate.get(); + } else { + throw new NoSuchElementException(String.format("Feature %s has no surrogate query defined! Use #hasSurrogateQueryFQN to check existence.", feature)); + } + } + + /** + * @return an unmodifiable set of features with registered surrogate queries + */ + public Set getRegisteredSurrogateQueries() { + return Collections.unmodifiableSet(getRegisteredSurrogateQueriesInternal()); + } + + private Set getRegisteredSurrogateQueriesInternal() { + return registeredSurrogateQueryMap.keySet(); + } + + /** + * @return an unmodifiable set of features with dynamically added surrogate queries + */ + public Set getDynamicSurrogateQueries() { + return Collections.unmodifiableSet(getDynamicSurrogateQueriesInternal()); + } + + private Set getDynamicSurrogateQueriesInternal() { + return dynamicSurrogateQueryMap.keySet(); + } + + /** + * @return an unmodifiable set that contains all features with surrogate queries. + */ + public Set getAllSurrogateQueries() { + Set results = new HashSet<>(getRegisteredSurrogateQueriesInternal()); + results.addAll(getDynamicSurrogateQueriesInternal()); + return results; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java new file mode 100644 index 00000000..66587f77 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/AbstractTrivialMaskedMemory.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.memories; + +import java.util.Iterator; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.matchers.util.IMemory; + +/** + * Common parts of nullary and identity specializations. + * + * @noextend This class is not intended to be subclassed by clients. + * @author Gabor Bergmann + * @since 2.0 + */ +abstract class AbstractTrivialMaskedMemory> extends MaskedTupleMemory { + + protected IMemory tuples; + + protected AbstractTrivialMaskedMemory(TupleMask mask, MemoryType bucketType, Object owner) { + super(mask, owner); + tuples = CollectionsFactory.createMemory(Object.class, bucketType); + } + + @Override + public Map> getWithTimeline(ITuple signature) { + throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); + } + + @Override + public void clear() { + tuples.clear(); + } + + @Override + public int getTotalSize() { + return tuples.size(); + } + + @Override + public Iterator iterator() { + return tuples.iterator(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java new file mode 100644 index 00000000..92081409 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/DefaultMaskedTupleMemory.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.memories; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; + +/** + * @author Gabor Bergmann + * + * Default implementation that covers all cases. + * + * @since 2.0 + */ +public final class DefaultMaskedTupleMemory> + extends MaskedTupleMemory { + /** + * Maps a signature tuple to the bucket of tuples with the given signature. + * + * @since 2.0 + */ + protected IMultiLookup signatureToTuples; + + /** + * @param mask + * The mask used to index the matchings + * @param owner + * the object "owning" this memory + * @param bucketType + * the kind of tuple collection maintained for each indexer bucket + * @since 2.0 + */ + public DefaultMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { + super(mask, owner); + signatureToTuples = CollectionsFactory. createMultiLookup(Object.class, bucketType, Object.class); + } + + @Override + public boolean add(Tuple tuple) { + Tuple signature = mask.transform(tuple); + return add(tuple, signature); + } + + @Override + public boolean add(Tuple tuple, Tuple signature) { + try { + return signatureToTuples.addPair(signature, tuple) == ChangeGranularity.KEY; + } catch (IllegalStateException ex) { // ignore worthless internal exception details + throw raiseDuplicateInsertion(tuple); + } + + } + + @Override + public boolean remove(Tuple tuple) { + Tuple signature = mask.transform(tuple); + return remove(tuple, signature); + } + + @Override + public boolean remove(Tuple tuple, Tuple signature) { + try { + return signatureToTuples.removePair(signature, tuple) == ChangeGranularity.KEY; + } catch (IllegalStateException ex) { // ignore worthless internal exception details + throw raiseDuplicateDeletion(tuple); + } + } + + @Override + public Map> getWithTimeline(ITuple signature) { + throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); + } + + @Override + public Collection get(ITuple signature) { + IMemoryView bucket = signatureToTuples.lookupUnsafe(signature); + return bucket == null ? null : bucket.distinctValues(); + } + + @Override + public void clear() { + signatureToTuples.clear(); + } + + @Override + public Iterable getSignatures() { + return signatureToTuples.distinctKeys(); + } + + @Override + public Iterator iterator() { + return signatureToTuples.distinctValues().iterator(); + } + + @Override + public int getTotalSize() { + int i = 0; + for (Tuple key : signatureToTuples.distinctKeys()) { + i += signatureToTuples.lookup(key).size(); + } + return i; + } + + @Override + public int getKeysetSize() { + return signatureToTuples.countKeys(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java new file mode 100644 index 00000000..dc59daf5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/IdentityMaskedTupleMemory.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.memories; + +import java.util.Collection; +import java.util.Collections; + +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; + +/** + * Specialized for identity mask; tuples are stored as a simple set/multiset memory. + * + * @author Gabor Bergmann + * @since 2.0 + */ +public final class IdentityMaskedTupleMemory> extends AbstractTrivialMaskedMemory { + + /** + * @param mask + * The mask used to index the matchings + * @param owner the object "owning" this memory + * @param bucketType the kind of tuple collection maintained for each indexer bucket + * @since 2.0 + */ + public IdentityMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { + super(mask, bucketType, owner); + if (!mask.isIdentity()) throw new IllegalArgumentException(mask.toString()); + } + + @Override + public int getKeysetSize() { + return tuples.size(); + } + + @Override + public Iterable getSignatures() { + return tuples; + } + + @Override + public Collection get(ITuple signature) { + Tuple contained = tuples.theContainedVersionOfUnsafe(signature); + return contained != null ? + Collections.singleton(contained) : + null; + } + + @Override + public boolean remove(Tuple tuple, Tuple signature) { + return tuples.removeOne(tuple); + } + + @Override + public boolean remove(Tuple tuple) { + return tuples.removeOne(tuple); + } + + @Override + public boolean add(Tuple tuple, Tuple signature) { + return tuples.addOne(tuple); + } + + @Override + public boolean add(Tuple tuple) { + return tuples.addOne(tuple); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java new file mode 100644 index 00000000..62377624 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/MaskedTupleMemory.java @@ -0,0 +1,385 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.memories; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyDefaultMaskedTupleMemory; +import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyIdentityMaskedTupleMemory; +import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyNullaryMaskedTupleMemory; +import tools.refinery.viatra.runtime.matchers.memories.timely.TimelyUnaryMaskedTupleMemory; +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Clearable; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.resumable.MaskedResumable; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; + +/** + * Indexes a collection of tuples by their signature (i.e. footprint, projection) obtained according to a mask. May + * belong to an "owner" (for documentation / traceability purposes). + *

+ * There are timeless and timely versions of the different memories. Timely versions associate {@link Timeline}s with + * the stored tuples. + * + * @noextend This class is not intended to be subclassed by clients. + * @author Gabor Bergmann + * @author Tamas Szabo + * @since 2.0 + */ +public abstract class MaskedTupleMemory> + implements Clearable, MaskedResumable { + + /** + * Creates a new memory for the given owner that indexes tuples according to the given mask. + */ + public static > MaskedTupleMemory create(final TupleMask mask, + final MemoryType bucketType, final Object owner) { + return create(mask, bucketType, owner, false); + } + + /** + * Creates a new memory for the given owner that indexes tuples according to the given mask. Clients can specify if + * the created memory should be timely or not.
+ *
+ * Timely means that tuples are associated with a timeline. + * + * @since 2.3 + */ + public static > MaskedTupleMemory create(final TupleMask mask, + final MemoryType bucketType, final Object owner, final boolean isTimely) { + return create(mask, bucketType, owner, isTimely, false); + } + + /** + * Creates a new memory for the given owner that indexes tuples according to the given mask. Clients can specify if + * the created memory should be timely or not. In case of timely memory, clients can also specify if the memory is + * lazy or not.
+ *
+ * Timely means that tuples are associated with a timeline.
+ *
+ * Lazyness can only be used together with timely memories. It means that the maintenance of the timelines is lazy, + * that is, the memory only updates its internal data structures at the timestamp affected by an update, and can be + * instructed later to resume the maintenance at higher timestamps, as well. + * + * @since 2.4 + */ + public static > MaskedTupleMemory create(final TupleMask mask, + final MemoryType bucketType, final Object owner, final boolean isTimely, final boolean isLazy) { + if (isTimely) { + if (bucketType != MemoryType.SETS) { + throw new IllegalArgumentException("Timely memories only support SETS as the bucket type!"); + } + if (mask.isIdentity()) { + return new TimelyIdentityMaskedTupleMemory(mask, owner, isLazy); + } else if (0 == mask.getSize()) { + return new TimelyNullaryMaskedTupleMemory(mask, owner, isLazy); + } else if (1 == mask.getSize()) { + return new TimelyUnaryMaskedTupleMemory(mask, owner, isLazy); + } else { + return new TimelyDefaultMaskedTupleMemory(mask, owner, isLazy); + } + } else { + if (isLazy) { + throw new IllegalArgumentException("Lazy maintenance is only supported by timely memories!"); + } + if (mask.isIdentity()) { + return new IdentityMaskedTupleMemory(mask, bucketType, owner); + } else if (0 == mask.getSize()) { + return new NullaryMaskedTupleMemory(mask, bucketType, owner); + } else if (1 == mask.getSize()) { + return new UnaryMaskedTupleMemory(mask, bucketType, owner); + } else { + return new DefaultMaskedTupleMemory(mask, bucketType, owner); + } + } + } + + @Override + public Map>> resumeAt(final Timestamp timestamp) { + throw new UnsupportedOperationException("This is only supported by lazy timely memory implementations!"); + } + + @Override + public Iterable getResumableSignatures() { + throw new UnsupportedOperationException("This is only supported by lazy timely memory implementations!"); + } + + @Override + public Timestamp getResumableTimestamp() { + return null; + } + + /** + * Initializes the contents of this memory based on the contents of another memory. The default value is associated + * with each tuple in the timely memories. + * + * @since 2.3 + */ + public void initializeWith(final MaskedTupleMemory other, final Timestamp defaultValue) { + throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); + } + + /** + * Returns true if there is any tuple with the given signature that is present at the timestamp +inf, false + * otherwise. + * @since 2.4 + */ + public boolean isPresentAtInfinity(final ITuple signature) { + return get(signature) != null; + } + + /** + * Returns true of this memory is timely, false otherwise. + * + * @since 2.3 + */ + public boolean isTimely() { + return false; + } + + /** + * The mask by which the tuples are indexed. + */ + protected final TupleMask mask; + + /** + * The object "owning" this memory. May be null. + * + * @since 1.7 + */ + protected final Object owner; + + /** + * The node owning this memory. May be null. + * + * @since 2.0 + */ + public Object getOwner() { + return owner; + } + + /** + * The mask according to which tuples are projected and indexed. + * + * @since 2.0 + */ + public TupleMask getMask() { + return mask; + } + + /** + * @return the number of distinct signatures of all stored tuples. + */ + public abstract int getKeysetSize(); + + /** + * @return the total number of distinct tuples stored. Multiple copies of the same tuple, if allowed, are counted as + * one. + * + *

+ * This is currently not cached but computed on demand. It is therefore not efficient, and shall only be + * used for debug / profiling purposes. + */ + public abstract int getTotalSize(); + + /** + * Iterates over distinct tuples stored in the memory, regardless of their signatures. + */ + public abstract Iterator iterator(); + + /** + * Retrieves a read-only view of exactly those signatures for which at least one tuple is stored + * + * @since 2.0 + */ + public abstract Iterable getSignatures(); + + /** + * Retrieves tuples that have the specified signature + * + * @return collection of tuples found, null if none + */ + public abstract Collection get(final ITuple signature); + + /** + * Retrieves the tuples and their associated timelines that have the specified signature. + * + * @return the mappings from tuples to timelines, null if there is no mapping for the signature + * @since 2.4 + */ + public abstract Map> getWithTimeline(final ITuple signature); + + /** + * Retrieves tuples that have the specified signature. + * + * @return collection of tuples found, never null + * @since 2.1 + */ + public Collection getOrEmpty(final ITuple signature) { + final Collection result = get(signature); + return result == null ? Collections.emptySet() : result; + } + + /** + * Retrieves tuples with their associated timelines that have the specified signature. + * + * @return map of tuples and timelines found, never null + * @since 2.4 + */ + public Map> getOrEmptyWithTimeline(final ITuple signature) { + final Map> result = getWithTimeline(signature); + return result == null ? Collections.emptyMap() : result; + } + + /** + * Removes a tuple occurrence from the memory with the given signature. + * + * @param tuple + * the tuple to be removed from the memory + * @param signature + * precomputed footprint of the tuple according to the mask + * + * @return true if this was the the last occurrence of the signature (according to the mask) + */ + public boolean remove(final Tuple tuple, final Tuple signature) { + throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); + } + + /** + * Removes a tuple occurrence from the memory with the given signature and timestamp. + * + * @param tuple + * the tuple to be removed from the memory + * @param signature + * precomputed footprint of the tuple according to the mask + * @param timestamp + * the timestamp associated with the tuple + * + * @return A {@link Diff} describing how the timeline of the given tuple changed. + * + * @since 2.4 + */ + public Diff removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { + throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); + } + + /** + * Removes a tuple occurrence from the memory. + * + * @param tuple + * the tuple to be removed from the memory + * + * @return true if this was the the last occurrence of the signature (according to the mask) + */ + public boolean remove(final Tuple tuple) { + throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); + } + + /** + * Removes a tuple occurrence from the memory with the given timestamp. + * + * @param tuple + * the tuple to be removed from the memory + * @param timestamp + * the timestamp associated with the tuple + * + * @return A {@link Diff} describing how the timeline of the given tuple changed. + * + * @since 2.4 + */ + public Diff removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { + throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); + } + + /** + * Adds a tuple occurrence to the memory with the given signature. + * + * @param tuple + * the tuple to be added to the memory + * @param signature + * precomputed footprint of the tuple according to the mask + * + * @return true if new signature encountered (according to the mask) + */ + public boolean add(final Tuple tuple, final Tuple signature) { + throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); + } + + /** + * Adds a tuple occurrence to the memory with the given signature and timestamp. + * + * @param tuple + * the tuple to be added to the memory + * @param signature + * precomputed footprint of the tuple according to the mask + * @param timestamp + * the timestamp associated with the tuple + * + * @return A {@link Diff} describing how the timeline of the given tuple changed. + * + * @since 2.4 + */ + public Diff addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { + throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); + } + + /** + * Adds a tuple occurrence to the memory. + * + * @param tuple + * the tuple to be added to the memory + * + * @return true if new signature encountered (according to the mask) + */ + public boolean add(final Tuple tuple) { + throw new UnsupportedOperationException("This is only supported by timeless memory implementations!"); + } + + /** + * Adds a tuple occurrence to the memory with the given timestamp. + * + * @param tuple + * the tuple to be added to the memory + * @param timestamp + * the timestamp associated with the tuple + * + * @return A {@link Diff} describing how the timeline of the given tuple changed. + * + * @since 2.4 + */ + public Diff addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { + throw new UnsupportedOperationException("This is only supported by timely memory implementations!"); + } + + protected MaskedTupleMemory(final TupleMask mask, final Object owner) { + super(); + this.mask = mask; + this.owner = owner; + } + + protected IllegalStateException raiseDuplicateInsertion(final Tuple tuple) { + return new IllegalStateException(String.format("Duplicate insertion of tuple %s into %s", tuple, owner)); + } + + protected IllegalStateException raiseDuplicateDeletion(final Tuple tuple) { + return new IllegalStateException(String.format("Duplicate deletion of tuple %s from %s", tuple, owner)); + } + + @Override + public String toString() { + return getClass().getSimpleName() + "<" + mask + ">@" + owner; + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java new file mode 100644 index 00000000..7fa9e053 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/NullaryMaskedTupleMemory.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.memories; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; + +/** + * Specialized for nullary mask; tuples are stored as a simple set/multiset memory. + * + * @author Gabor Bergmann + * @since 2.0 + */ +public final class NullaryMaskedTupleMemory> extends AbstractTrivialMaskedMemory { + + protected static final Set UNIT_RELATION = + Collections.singleton(Tuples.staticArityFlatTupleOf()); + protected static final Set EMPTY_RELATION = + Collections.emptySet(); + /** + * @param mask + * The mask used to index the matchings + * @param owner the object "owning" this memory + * @param bucketType the kind of tuple collection maintained for each indexer bucket + * @since 2.0 + */ + public NullaryMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { + super(mask, bucketType, owner); + if (0 != mask.getSize()) throw new IllegalArgumentException(mask.toString()); + } + + @Override + public int getKeysetSize() { + return tuples.isEmpty() ? 0 : 1; + } + + @Override + public Iterable getSignatures() { + return tuples.isEmpty() ? EMPTY_RELATION : UNIT_RELATION; + } + + @Override + public Collection get(ITuple signature) { + if (0 == signature.getSize()) + return tuples.distinctValues(); + else return null; + } + + @Override + public boolean remove(Tuple tuple, Tuple signature) { + tuples.removeOne(tuple); + return tuples.isEmpty(); + } + + @Override + public boolean remove(Tuple tuple) { + return remove(tuple, null); + } + + @Override + public boolean add(Tuple tuple, Tuple signature) { + boolean wasEmpty = tuples.isEmpty(); + tuples.addOne(tuple); + return wasEmpty; + } + + @Override + public boolean add(Tuple tuple) { + return add(tuple, null); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java new file mode 100644 index 00000000..f34cc9e3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/UnaryMaskedTupleMemory.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.memories; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; + +/** + * Specialized for unary mask; tuples are indexed by a single column as opposed to a projection (signature) tuple. + * + * @author Gabor Bergmann + * @since 2.0 + */ +public final class UnaryMaskedTupleMemory> extends MaskedTupleMemory { + + protected IMultiLookup columnToTuples; + protected final int keyPosition; + + /** + * @param mask + * The mask used to index the matchings + * @param owner the object "owning" this memory + * @param bucketType the kind of tuple collection maintained for each indexer bucket + * @since 2.0 + */ + public UnaryMaskedTupleMemory(TupleMask mask, MemoryType bucketType, Object owner) { + super(mask, owner); + if (1 != mask.getSize()) throw new IllegalArgumentException(mask.toString()); + + columnToTuples = CollectionsFactory.createMultiLookup( + Object.class, bucketType, Object.class); + keyPosition = mask.indices[0]; + } + + @Override + public void clear() { + columnToTuples.clear(); + } + + @Override + public int getKeysetSize() { + return columnToTuples.countKeys(); + } + + @Override + public int getTotalSize() { + int i = 0; + for (Object key : columnToTuples.distinctKeys()) { + i += columnToTuples.lookup(key).size(); + } + return i; + } + + @Override + public Iterator iterator() { + return columnToTuples.distinctValues().iterator(); + } + + @Override + public Iterable getSignatures() { + return () -> { + Iterator wrapped = columnToTuples.distinctKeys().iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return wrapped.hasNext(); + } + @Override + public Tuple next() { + Object key = wrapped.next(); + return Tuples.staticArityFlatTupleOf(key); + } + }; + }; + } + + @Override + public Collection get(ITuple signature) { + Object key = signature.get(0); + IMemoryView bucket = columnToTuples.lookup(key); + return bucket == null ? null : bucket.distinctValues(); + } + + @Override + public Map> getWithTimeline(ITuple signature) { + throw new UnsupportedOperationException("Timeless memories do not support timestamp-based lookup!"); + } + + @Override + public boolean remove(Tuple tuple, Tuple signature) { + return removeInternal(tuple, tuple.get(keyPosition)); + } + + @Override + public boolean remove(Tuple tuple) { + return removeInternal(tuple, tuple.get(keyPosition)); + } + + @Override + public boolean add(Tuple tuple, Tuple signature) { + return addInternal(tuple, tuple.get(keyPosition)); + } + + @Override + public boolean add(Tuple tuple) { + return addInternal(tuple, tuple.get(keyPosition)); + } + + protected boolean addInternal(Tuple tuple, Object key) { + try { + return columnToTuples.addPair(key, tuple) == ChangeGranularity.KEY; + } catch (IllegalStateException ex) { // ignore worthless internal exception details + throw raiseDuplicateInsertion(tuple); + } + } + + protected boolean removeInternal(Tuple tuple, Object key) { + try { + return columnToTuples.removePair(key, tuple) == ChangeGranularity.KEY; + } catch (IllegalStateException ex) { // ignore worthless internal exception details + throw raiseDuplicateDeletion(tuple); + } + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java new file mode 100644 index 00000000..45ce3a4e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyMaskedMemory.java @@ -0,0 +1,228 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.memories.timely; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.TreeMap; + +import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; + +/** + * Common parts of timely default and timely unary implementations. + * + * @noextend This class is not intended to be subclassed by clients. + * @author Tamas Szabo + * @since 2.3 + */ +abstract class AbstractTimelyMaskedMemory, KeyType> + extends MaskedTupleMemory { + + protected final TreeMap> foldingStates; + protected final Map> memoryMap; + protected final boolean isLazy; + + public AbstractTimelyMaskedMemory(final TupleMask mask, final Object owner, final boolean isLazy) { + super(mask, owner); + this.isLazy = isLazy; + this.memoryMap = CollectionsFactory.createMap(); + this.foldingStates = this.isLazy ? CollectionsFactory.createTreeMap() : null; + } + + @Override + public void initializeWith(final MaskedTupleMemory other, final Timestamp defaultValue) { + final Iterable signatures = other.getSignatures(); + for (final Tuple signature : signatures) { + if (other.isTimely()) { + final Map> tupleMap = other.getWithTimeline(signature); + for (final Entry> entry : tupleMap.entrySet()) { + for (final Signed signed : entry.getValue().asChangeSequence()) { + if (signed.getDirection() == Direction.DELETE) { + this.removeWithTimestamp(entry.getKey(), signed.getPayload()); + } else { + this.addWithTimestamp(entry.getKey(), signed.getPayload()); + } + } + } + } else { + final Collection tuples = other.get(signature); + for (final Tuple tuple : tuples) { + this.addWithTimestamp(tuple, defaultValue); + } + } + } + } + + public boolean isPresentAtInfinityInteral(KeyType key) { + final TimelyMemory values = this.memoryMap.get(key); + if (values == null) { + return false; + } else { + return values.getCountAtInfinity() != 0; + } + } + + @Override + public void clear() { + this.memoryMap.clear(); + } + + @Override + public int getKeysetSize() { + return this.memoryMap.keySet().size(); + } + + @Override + public int getTotalSize() { + int i = 0; + for (final Entry> entry : this.memoryMap.entrySet()) { + i += entry.getValue().size(); + } + return i; + } + + @Override + public Iterator iterator() { + return this.memoryMap.values().stream().flatMap(e -> e.keySet().stream()).iterator(); + } + + protected Collection getInternal(final KeyType key) { + final TimelyMemory memory = this.memoryMap.get(key); + if (memory == null) { + return null; + } else { + return memory.getTuplesAtInfinity(); + } + } + + public Map> getWithTimestampInternal(final KeyType key) { + final TimelyMemory memory = this.memoryMap.get(key); + if (memory == null) { + return null; + } else { + return memory.asMap(); + } + } + + protected Diff removeInternal(final KeyType key, final Tuple tuple, final Timestamp timestamp) { + Timestamp oldResumableTimestamp = null; + Timestamp newResumableTimestamp = null; + + final TimelyMemory keyMemory = this.memoryMap.get(key); + if (keyMemory == null) { + throw raiseDuplicateDeletion(tuple); + } + + if (this.isLazy) { + oldResumableTimestamp = keyMemory.getResumableTimestamp(); + } + + Diff diff = null; + try { + diff = keyMemory.remove(tuple, timestamp); + } catch (final IllegalStateException e) { + throw raiseDuplicateDeletion(tuple); + } + if (keyMemory.isEmpty()) { + this.memoryMap.remove(key); + } + + if (this.isLazy) { + newResumableTimestamp = keyMemory.getResumableTimestamp(); + if (!Objects.equals(oldResumableTimestamp, newResumableTimestamp)) { + unregisterFoldingState(oldResumableTimestamp, key); + registerFoldingState(newResumableTimestamp, key); + } + } + + return diff; + } + + protected Diff addInternal(final KeyType key, final Tuple tuple, final Timestamp timestamp) { + Timestamp oldResumableTimestamp = null; + Timestamp newResumableTimestamp = null; + + final TimelyMemory keyMemory = this.memoryMap.computeIfAbsent(key, + k -> new TimelyMemory(this.isLazy)); + + if (this.isLazy) { + oldResumableTimestamp = keyMemory.getResumableTimestamp(); + } + + final Diff diff = keyMemory.put(tuple, timestamp); + + if (this.isLazy) { + newResumableTimestamp = keyMemory.getResumableTimestamp(); + if (!Objects.equals(oldResumableTimestamp, newResumableTimestamp)) { + unregisterFoldingState(oldResumableTimestamp, key); + registerFoldingState(newResumableTimestamp, key); + } + } + + return diff; + } + + @Override + public Diff removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { + return removeWithTimestamp(tuple, null, timestamp); + } + + @Override + public Diff addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { + return addWithTimestamp(tuple, null, timestamp); + } + + @Override + public boolean isTimely() { + return true; + } + + protected void registerFoldingState(final Timestamp timestamp, final KeyType key) { + if (timestamp != null) { + this.foldingStates.compute(timestamp, (k, v) -> { + if (v == null) { + v = CollectionsFactory.createSet(); + } + v.add(key); + return v; + }); + } + } + + protected void unregisterFoldingState(final Timestamp timestamp, final KeyType key) { + if (timestamp != null) { + this.foldingStates.compute(timestamp, (k, v) -> { + v.remove(key); + return v.isEmpty() ? null : v; + }); + } + } + + @Override + public Timestamp getResumableTimestamp() { + if (this.foldingStates == null || this.foldingStates.isEmpty()) { + return null; + } else { + return this.foldingStates.firstKey(); + } + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java new file mode 100644 index 00000000..ca06685a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/AbstractTimelyTrivialMaskedMemory.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.memories.timely; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.memories.MaskedTupleMemory; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; +import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; + +/** + * Common parts of timely nullary and timely identity implementations. + * + * @noextend This class is not intended to be subclassed by clients. + * @author Tamas Szabo + * @since 2.3 + */ +abstract class AbstractTimelyTrivialMaskedMemory> extends MaskedTupleMemory { + + protected final TimelyMemory memory; + + protected AbstractTimelyTrivialMaskedMemory(final TupleMask mask, final Object owner, final boolean isLazy) { + super(mask, owner); + this.memory = new TimelyMemory(isLazy); + } + + @Override + public void initializeWith(final MaskedTupleMemory other, final Timestamp defaultValue) { + final Iterable signatures = other.getSignatures(); + for (final Tuple signature : signatures) { + if (other.isTimely()) { + final Map> tupleMap = other.getWithTimeline(signature); + for (final Entry> entry : tupleMap.entrySet()) { + for (final Signed signed : entry.getValue().asChangeSequence()) { + if (signed.getDirection() == Direction.DELETE) { + this.removeWithTimestamp(entry.getKey(), signed.getPayload()); + } else { + this.addWithTimestamp(entry.getKey(), signed.getPayload()); + } + } + } + } else { + final Collection tuples = other.get(signature); + for (final Tuple tuple : tuples) { + this.removeWithTimestamp(tuple, defaultValue); + } + } + } + } + + @Override + public void clear() { + this.memory.clear(); + } + + @Override + public int getTotalSize() { + return this.memory.size(); + } + + @Override + public Iterator iterator() { + return this.memory.keySet().iterator(); + } + + @Override + public Diff removeWithTimestamp(final Tuple tuple, final Timestamp timestamp) { + return removeWithTimestamp(tuple, null, timestamp); + } + + @Override + public Diff addWithTimestamp(final Tuple tuple, final Timestamp timestamp) { + return addWithTimestamp(tuple, null, timestamp); + } + + @Override + public boolean isTimely() { + return true; + } + + @Override + public Timestamp getResumableTimestamp() { + return this.memory.getResumableTimestamp(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java new file mode 100644 index 00000000..623d7399 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyDefaultMaskedTupleMemory.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.memories.timely; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; + +/** + * Default timely implementation that covers all cases. + * + * @author Tamas Szabo + * @since 2.3 + */ +public final class TimelyDefaultMaskedTupleMemory> + extends AbstractTimelyMaskedMemory { + + public TimelyDefaultMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { + super(mask, owner, isLazy); + } + + @Override + public Iterable getSignatures() { + return this.memoryMap.keySet(); + } + + @Override + public Diff removeWithTimestamp(final Tuple tuple, final Tuple signature, + final Timestamp timestamp) { + final Tuple key = mask.transform(tuple); + return removeInternal(key, tuple, timestamp); + } + + @Override + public Diff addWithTimestamp(final Tuple tuple, final Tuple signature, + final Timestamp timestamp) { + final Tuple key = this.mask.transform(tuple); + return addInternal(key, tuple, timestamp); + } + + @Override + public Collection get(final ITuple signature) { + return getInternal(signature.toImmutable()); + } + + @Override + public Map> getWithTimeline(final ITuple signature) { + return getWithTimestampInternal(signature.toImmutable()); + } + + @Override + public boolean isPresentAtInfinity(final ITuple signature) { + return isPresentAtInfinityInteral(signature.toImmutable()); + } + + @Override + public Set getResumableSignatures() { + if (this.foldingStates == null || this.foldingStates.isEmpty()) { + return Collections.emptySet(); + } else { + return this.foldingStates.firstEntry().getValue(); + } + } + + @Override + public Map>> resumeAt(final Timestamp timestamp) { + final Map>> result = CollectionsFactory.createMap(); + final Timestamp resumableTimestamp = this.getResumableTimestamp(); + if (resumableTimestamp == null || resumableTimestamp.compareTo(timestamp) != 0) { + throw new IllegalStateException("Expected to continue folding at " + resumableTimestamp + "!"); + } + final Set signatures = this.foldingStates.remove(timestamp); + for (final Tuple signature : signatures) { + final TimelyMemory memory = this.memoryMap.get(signature); + final Map> diffMap = memory.resumeAt(resumableTimestamp); + result.put(signature, diffMap); + registerFoldingState(memory.getResumableTimestamp(), signature); + } + return result; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java new file mode 100644 index 00000000..568f274d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyIdentityMaskedTupleMemory.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.memories.timely; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; + +/** + * Timely specialization for identity mask. + * + * @author Tamas Szabo + * @since 2.3 + */ +public final class TimelyIdentityMaskedTupleMemory> + extends AbstractTimelyTrivialMaskedMemory { + + public TimelyIdentityMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { + super(mask, owner, isLazy); + if (!mask.isIdentity()) + throw new IllegalArgumentException(mask.toString()); + } + + @Override + public int getKeysetSize() { + return this.memory.size(); + } + + @Override + public Iterable getSignatures() { + return this.memory.keySet(); + } + + @Override + public Collection get(final ITuple signature) { + if (this.memory.getTuplesAtInfinity().contains(signature)) { + return Collections.singleton(signature.toImmutable()); + } else { + return null; + } + } + + @Override + public Map> getWithTimeline(final ITuple signature) { + final Timeline value = this.memory.get(signature); + if (value != null) { + return Collections.singletonMap(signature.toImmutable(), value); + } else { + return null; + } + } + + @Override + public Diff removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { + try { + return this.memory.remove(tuple, timestamp); + } catch (final IllegalStateException e) { + throw raiseDuplicateDeletion(tuple); + } + } + + @Override + public Diff addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { + return this.memory.put(tuple, timestamp); + } + + @Override + public boolean isPresentAtInfinity(final ITuple signature) { + return this.memory.isPresentAtInfinity(signature.toImmutable()); + } + + @Override + public Set getResumableSignatures() { + if (this.memory.getResumableTimestamp() != null) { + return this.memory.getResumableTuples(); + } else { + return Collections.emptySet(); + } + } + + @Override + public Map>> resumeAt(final Timestamp timestamp) { + final Map> diffMap = this.memory.resumeAt(timestamp); + final Map>> result = CollectionsFactory.createMap(); + for (final Entry> entry : diffMap.entrySet()) { + result.put(entry.getKey(), Collections.singletonMap(entry.getKey(), entry.getValue())); + } + return result; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java new file mode 100644 index 00000000..75987a89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyNullaryMaskedTupleMemory.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.memories.timely; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; + +/** + * Timely specialization for nullary mask. + * + * @author Tamas Szabo + * @since 2.3 + */ +public final class TimelyNullaryMaskedTupleMemory> + extends AbstractTimelyTrivialMaskedMemory { + + protected static final Tuple EMPTY_TUPLE = Tuples.staticArityFlatTupleOf(); + protected static final Set UNIT_RELATION = Collections.singleton(EMPTY_TUPLE); + protected static final Set EMPTY_RELATION = Collections.emptySet(); + + public TimelyNullaryMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { + super(mask, owner, isLazy); + if (0 != mask.getSize()) { + throw new IllegalArgumentException(mask.toString()); + } + } + + @Override + public int getKeysetSize() { + return this.memory.isEmpty() ? 0 : 1; + } + + @Override + public Iterable getSignatures() { + return this.memory.isEmpty() ? EMPTY_RELATION : UNIT_RELATION; + } + + @Override + public Collection get(final ITuple signature) { + if (0 == signature.getSize()) { + return this.memory.getTuplesAtInfinity(); + } else { + return null; + } + } + + @Override + public Map> getWithTimeline(final ITuple signature) { + if (0 == signature.getSize()) { + return this.memory.asMap(); + } else { + return null; + } + } + + @Override + public Diff removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { + try { + return this.memory.remove(tuple, timestamp); + } catch (final IllegalStateException e) { + throw raiseDuplicateDeletion(tuple); + } + } + + @Override + public Diff addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { + return this.memory.put(tuple, timestamp); + } + + @Override + public boolean isPresentAtInfinity(final ITuple signature) { + if (0 == signature.getSize()) { + return this.memory.getCountAtInfinity() > 0; + } else { + return false; + } + } + + @Override + public Set getResumableSignatures() { + if (this.memory.getResumableTimestamp() != null) { + return UNIT_RELATION; + } else { + return EMPTY_RELATION; + } + } + + @Override + public Map>> resumeAt(final Timestamp timestamp) { + return Collections.singletonMap(EMPTY_TUPLE, this.memory.resumeAt(timestamp)); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java new file mode 100644 index 00000000..178193af --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/memories/timely/TimelyUnaryMaskedTupleMemory.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.memories.timely; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.TimelyMemory; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; + +/** + * Timely specialization for unary mask. + * + * @author Tamas Szabo + * @since 2.3 + */ +public final class TimelyUnaryMaskedTupleMemory> + extends AbstractTimelyMaskedMemory { + + protected final int keyPosition; + + public TimelyUnaryMaskedTupleMemory(final TupleMask mask, final Object owner, final boolean isLazy) { + super(mask, owner, isLazy); + if (1 != mask.getSize()) + throw new IllegalArgumentException(mask.toString()); + this.keyPosition = mask.indices[0]; + } + + @Override + public Iterable getSignatures() { + return () -> { + final Iterator wrapped = this.memoryMap.keySet().iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return wrapped.hasNext(); + } + + @Override + public Tuple next() { + final Object key = wrapped.next(); + return Tuples.staticArityFlatTupleOf(key); + } + }; + }; + } + + @Override + public Diff removeWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { + final Object key = tuple.get(keyPosition); + return removeInternal(key, tuple, timestamp); + } + + @Override + public Diff addWithTimestamp(final Tuple tuple, final Tuple signature, final Timestamp timestamp) { + final Object key = tuple.get(keyPosition); + return addInternal(key, tuple, timestamp); + } + + @Override + public Collection get(final ITuple signature) { + return getInternal(signature.get(0)); + } + + @Override + public Map> getWithTimeline(final ITuple signature) { + return getWithTimestampInternal(signature.get(0)); + } + + @Override + public boolean isPresentAtInfinity(ITuple signature) { + return isPresentAtInfinityInteral(signature.get(0)); + } + + @Override + public Iterable getResumableSignatures() { + if (this.foldingStates == null || this.foldingStates.isEmpty()) { + return Collections.emptySet(); + } else { + return () -> { + final Iterator wrapped = this.foldingStates.firstEntry().getValue().iterator(); + return new Iterator() { + @Override + public boolean hasNext() { + return wrapped.hasNext(); + } + + @Override + public Tuple next() { + final Object key = wrapped.next(); + return Tuples.staticArityFlatTupleOf(key); + } + }; + }; + } + } + + @Override + public Map>> resumeAt(final Timestamp timestamp) { + final Map>> result = CollectionsFactory.createMap(); + final Timestamp resumableTimestamp = this.getResumableTimestamp(); + if (resumableTimestamp == null) { + throw new IllegalStateException("There is nothing to fold!"); + } else if (resumableTimestamp.compareTo(timestamp) != 0) { + throw new IllegalStateException("Expected to continue folding at " + resumableTimestamp + "!"); + } + + final Set signatures = this.foldingStates.remove(timestamp); + for (final Object signature : signatures) { + final TimelyMemory memory = this.memoryMap.get(signature); + final Map> diffMap = memory.resumeAt(resumableTimestamp); + result.put(Tuples.staticArityFlatTupleOf(signature), diffMap); + registerFoldingState(memory.getResumableTimestamp(), signature); + } + return result; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java new file mode 100644 index 00000000..c9f4b305 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IOperationCompiler.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.planning; + +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; + +/** + * + * An implicit common parameter is the "effort" PatternDescription. This + * indicates that the build request is part of an effort to build the matcher of + * the given pattern; it it important to record this during code generation so + * that the generated code can be separated according to patterns. + * + * @param + * the handle of a receiver-like RETE ending to which plans can be + * connected + * @author Gabor Bergmann + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IOperationCompiler { + + /** + * @throws ViatraQueryRuntimeException + */ + public Collector patternCollector(PQuery pattern); + + public void buildConnection(SubPlan parentPlan, Collector collector); + + /** + * @since 0.9 + */ + public void patternFinished(PQuery pattern, Collector collector); + + /** + * @throws ViatraQueryRuntimeException + */ + public SubPlan patternCallPlan(Tuple nodes, PQuery supplierKey); + + public SubPlan transitiveInstantiationPlan(Tuple nodes); + + public SubPlan directInstantiationPlan(Tuple nodes); + + public SubPlan transitiveGeneralizationPlan(Tuple nodes); + + public SubPlan directGeneralizationPlan(Tuple nodes); + + public SubPlan transitiveContainmentPlan(Tuple nodes); + + public SubPlan directContainmentPlan(Tuple nodes); + + public SubPlan binaryEdgeTypePlan(Tuple nodes, Object supplierKey); + + public SubPlan ternaryEdgeTypePlan(Tuple nodes, Object supplierKey); + + public SubPlan unaryTypePlan(Tuple nodes, Object supplierKey); + + public SubPlan buildStartingPlan(Object[] constantValues, Object[] constantNames); + + public SubPlan buildEqualityChecker(SubPlan parentPlan, int[] indices); + + public SubPlan buildInjectivityChecker(SubPlan parentPlan, int subject, int[] inequalIndices); + + public SubPlan buildTransitiveClosure(SubPlan parentPlan); + + public SubPlan buildTrimmer(SubPlan parentPlan, TupleMask trimMask, boolean enforceUniqueness); + + public SubPlan buildBetaNode(SubPlan primaryPlan, SubPlan sidePlan, + TupleMask primaryMask, TupleMask sideMask, TupleMask complementer, boolean negative); + + public SubPlan buildCounterBetaNode(SubPlan primaryPlan, SubPlan sidePlan, + TupleMask primaryMask, TupleMask originalSideMask, TupleMask complementer, + Object aggregateResultCalibrationElement); + + public SubPlan buildCountCheckBetaNode(SubPlan primaryPlan, SubPlan sidePlan, + TupleMask primaryMask, TupleMask originalSideMask, int resultPositionInSignature); + + public SubPlan buildPredicateChecker(IExpressionEvaluator evaluator, Map tupleNameMap, + SubPlan parentPlan); + public SubPlan buildFunctionEvaluator(IExpressionEvaluator evaluator, Map tupleNameMap, + SubPlan parentPlan, Object computedResultCalibrationElement); + + /** + * @return an operation compiler that potentially acts on a separate container + */ + public IOperationCompiler getNextContainer(); + + /** + * @return an operation compiler that puts build actions on the tab of the given pattern + * @since 0.9 + */ + public IOperationCompiler putOnTab(PQuery effort /*, IPatternMatcherContext context*/); + + public void reinitialize(); + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java new file mode 100644 index 00000000..6ce9d91b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/IQueryPlannerStrategy.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.planning; + +import org.apache.log4j.Logger; +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; + +/** + * An algorithm that builds a query plan based on a PSystem representation of a body of constraints. This interface is + * for internal use of the various query backends. + * + * @author Gabor Bergmann + */ +public interface IQueryPlannerStrategy { + + /** + * @throws ViatraQueryRuntimeException + */ + public SubPlan plan(PBody pSystem, Logger logger, IQueryMetaContext context); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java new file mode 100644 index 00000000..501ddf73 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/QueryProcessingException.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.planning; + +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; + +/** + * @author Zoltan Ujhelyi + * @since 0.9 + */ +public class QueryProcessingException extends ViatraQueryRuntimeException { + + private static final long serialVersionUID = -8272290113656867086L; + /** + * Binding the '{n}' (n = 1..N) strings to contextual conditions in 'context' + * + * @param context + * : array of context-sensitive Strings + */ + protected static String bind(String message, String[] context) { + if (context == null) + return message; + + String internal = message; + for (int i = 0; i < context.length; i++) { + internal = internal.replace("{" + (i + 1) + "}", context[i] != null ? context[i] : "<>"); + } + return internal; + } + + private Object patternDescription; + private String shortMessage; + + /** + * @param message + * The template of the exception message + * @param context + * The data elements to be used to instantiate the template. Can be null if no context parameter is + * defined + * @param patternDescription + * the PatternDescription where the exception occurred + * @since 2.0 + */ + public QueryProcessingException(String message, Object patternDescription) { + super(message); + initializeFields(message, patternDescription); + } + + /** + * @param message + * The template of the exception message + * @param context + * The data elements to be used to instantiate the template. Can be null if no context parameter is + * defined + * @param patternDescription + * the PatternDescription where the exception occurred + */ + public QueryProcessingException(String message, String[] context, String shortMessage, Object patternDescription) { + super(bind(message, context)); + initializeFields(shortMessage, patternDescription); + } + + /** + * @param message + * The template of the exception message + * @param context + * The data elements to be used to instantiate the template. Can be null if no context parameter is + * defined + * @param patternDescription + * the PatternDescription where the exception occurred + */ + public QueryProcessingException(String message, String[] context, String shortMessage, Object patternDescription, + Throwable cause) { + super(bind(message, context), cause); + initializeFields(shortMessage, patternDescription); + } + + public Object getPatternDescription() { + return patternDescription; + } + + public String getShortMessage() { + return shortMessage; + } + + private void initializeFields(String shortMessage, Object patternDescription) { + this.patternDescription = patternDescription; + this.shortMessage = shortMessage; + } + + + public void setPatternDescription(Object patternDescription) { + this.patternDescription = patternDescription; + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java new file mode 100644 index 00000000..1998df9d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlan.java @@ -0,0 +1,240 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.planning; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; +import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; +import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; +import tools.refinery.viatra.runtime.matchers.planning.operations.PStart; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; + +/** + * A plan representing a subset of (or possibly all the) constraints evaluated. A SubPlan instance is responsible for + * representing a state of the plan; but after it is initialized it is expected be immutable + * (exception: inferred constraints, see {@link #inferConstraint(PConstraint)}). + * + *

A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. + * Important maintained information:

    + *
  • set of variables whose values are known when the runtime evaluation is at this stage, + *
  • set of constraints that are known to hold true at this point. + *
+ * + *

Recommended to instantiate via a {@link SubPlanFactory} or subclasses, + * so that query planners can subclass SubPlan if needed. + * + * @author Gabor Bergmann + * + */ +public class SubPlan { + private PBody body; + private List parentPlans; + private POperation operation; + + private final Set visibleVariables; + private final Set allVariables; + private final Set introducedVariables; // delta compared to first parent + private Set allConstraints; + private Set deltaConstraints; // delta compared to all parents + private Set externallyInferredConstraints; // inferred in addition to direct consequences of the operation and parents + + + + + + /** + * A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. + */ + public SubPlan(PBody body, POperation operation, SubPlan... parentPlans) { + this(body, operation, Arrays.asList(parentPlans)); + } + /** + * A SubPlan is constructed by applying a {@link POperation} on zero or more parent SubPlans. + */ + public SubPlan(PBody body, POperation operation, List parentPlans) { + super(); + this.body = body; + this.parentPlans = parentPlans; + this.operation = operation; + + this.externallyInferredConstraints = new HashSet(); + this.deltaConstraints = new HashSet(operation.getDeltaConstraints()); + + this.allVariables = new HashSet(); + for (PConstraint constraint: deltaConstraints) { + this.allVariables.addAll(constraint.getDeducedVariables()); + } + this.allConstraints = new HashSet(deltaConstraints); + for (SubPlan parentPlan: parentPlans) { + this.allConstraints.addAll(parentPlan.getAllEnforcedConstraints()); + this.allVariables.addAll(parentPlan.getAllDeducedVariables()); + } + + // TODO this is ugly a bit + if (operation instanceof PStart) { + this.visibleVariables = new HashSet(((PStart) operation).getAPrioriVariables()); + this.allVariables.addAll(visibleVariables); + } else if (operation instanceof PProject) { + this.visibleVariables = new HashSet(((PProject) operation).getToVariables()); + } else { + this.visibleVariables = new HashSet(); + for (SubPlan parentPlan: parentPlans) + this.visibleVariables.addAll(parentPlan.getVisibleVariables()); + for (PConstraint constraint: deltaConstraints) + this.visibleVariables.addAll(constraint.getDeducedVariables()); + } + + this.introducedVariables = new HashSet(this.visibleVariables); + if (!parentPlans.isEmpty()) + introducedVariables.removeAll(parentPlans.get(0).getVisibleVariables()); + + operation.checkConsistency(this); + } + + + @Override + public String toString() { + return toLongString(); + } + public String toShortString() { + return String.format("Plan{%s}:%s", + visibleVariables.stream().map(PVariable::getName).collect(Collectors.joining(",")), + operation.getShortName()); + } + public String toLongString() { + return String.format("%s<%s>", + toShortString(), + parentPlans.stream().map(Object::toString).collect(Collectors.joining("; "))); + } + + + /** + * All constraints that are known to hold at this point + */ + public Set getAllEnforcedConstraints() { + return allConstraints; + } + + /** + * The new constraints enforced at this stage of plan, that aren't yet enforced at parents + * (results are also included in {@link SubPlan#getAllEnforcedConstraints()}) + */ + public Set getDeltaEnforcedConstraints() { + return deltaConstraints; + } + + /** + * Indicate that a given constraint was found to be automatically satisfied at this point + * without additional operations. + * (results are also included in {@link SubPlan#getDeltaEnforcedConstraints()}) + * + *

Warning: not propagated automatically to child plans, + * so best to invoke before constructing further SubPlans.

+ */ + public void inferConstraint(PConstraint constraint) { + externallyInferredConstraints.add(constraint); + deltaConstraints.add(constraint); + allConstraints.add(constraint); + } + + public PBody getBody() { + return body; + } + + /** + * Variables which are assigned a value at this point + * (results are also included in {@link SubPlan#getAllDeducedVariables()}) + */ + public Set getVisibleVariables() { + return visibleVariables; + } + /** + * Variables which have been assigned a value; + * includes visible variables (see {@link #getVisibleVariables()}) + * and additionally any variables hidden by a projection (see {@link PProject}). + */ + public Set getAllDeducedVariables() { + return allVariables; + } + /** + * Delta compared to first parent: variables that are visible here but were not visible at the first parent. + */ + public Set getIntroducedVariables() { + return introducedVariables; + } + public List getParentPlans() { + return parentPlans; + } + public POperation getOperation() { + return operation; + } + + + /** + * The closure of all type judgments of enforced constraints at this point. + *

No subsumption applied. + */ + public Set getAllImpliedTypeJudgements(IQueryMetaContext context) { + Set impliedJudgements = allImpliedTypeJudgements.get(context); + if (impliedJudgements == null) { + Set equivalentJudgements = TypeHelper.getDirectJudgements(getAllEnforcedConstraints(), context); + impliedJudgements = TypeHelper.typeClosure(equivalentJudgements, context); + + allImpliedTypeJudgements.put(context, impliedJudgements); + } + return impliedJudgements; + } + private WeakHashMap> allImpliedTypeJudgements = new WeakHashMap>(); + + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((operation == null) ? 0 : operation.hashCode()); + result = prime * result + + ((parentPlans == null) ? 0 : parentPlans.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof SubPlan)) + return false; + SubPlan other = (SubPlan) obj; + if (operation == null) { + if (other.operation != null) + return false; + } else if (!operation.equals(other.operation)) + return false; + if (parentPlans == null) { + if (other.parentPlans != null) + return false; + } else if (!parentPlans.equals(other.parentPlans)) + return false; + return true; + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java new file mode 100644 index 00000000..d0df5fac --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/SubPlanFactory.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.planning; + +import tools.refinery.viatra.runtime.matchers.planning.operations.POperation; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; + +/** + * Single entry point for creating subplans. + * Can be subclassed by query planner to provide specialized SubPlans. + * @author Bergmann Gabor + * + */ +public class SubPlanFactory { + + protected PBody body; + + public SubPlanFactory(PBody body) { + super(); + this.body = body; + } + + public SubPlan createSubPlan(POperation operation, SubPlan... parentPlans) { + return new SubPlan(body, operation, parentPlans); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java new file mode 100644 index 00000000..ed5d1cbb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/BuildHelper.java @@ -0,0 +1,165 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.planning.helpers; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.planning.SubPlanFactory; +import tools.refinery.viatra.runtime.matchers.planning.operations.PProject; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; + +/** + * @author Gabor Bergmann + * + */ +public class BuildHelper { + + private BuildHelper() { + // Hiding constructor for utility class + } + +// public static SubPlan naturalJoin(IOperationCompiler buildable, +// SubPlan primaryPlan, SubPlan secondaryPlan) { +// JoinHelper joinHelper = new JoinHelper(primaryPlan, secondaryPlan); +// return buildable.buildBetaNode(primaryPlan, secondaryPlan, joinHelper.getPrimaryMask(), +// joinHelper.getSecondaryMask(), joinHelper.getComplementerMask(), false); +// } + + + /** + * Reduces the number of tuples by trimming (existentially quantifying) the set of variables that

    + *
  • are visible in the subplan, + *
  • are not exported parameters, + *
  • have all their constraints already enforced in the subplan, + *
and thus will not be needed anymore. + * + * @param onlyIfNotDetermined if true, no trimming performed unless there is at least one variable that is not functionally determined + * @return the plan after the trimming (possibly the original) + * @since 1.5 + */ + public static SubPlan trimUnneccessaryVariables(SubPlanFactory planFactory, /*IOperationCompiler buildable,*/ + SubPlan plan, boolean onlyIfNotDetermined, QueryAnalyzer analyzer) { + Set canBeTrimmed = new HashSet(); + Set variablesInPlan = plan.getVisibleVariables(); + for (PVariable trimCandidate : variablesInPlan) { + if (trimCandidate.getReferringConstraintsOfType(ExportedParameter.class).isEmpty()) { + if (plan.getAllEnforcedConstraints().containsAll(trimCandidate.getReferringConstraints())) + canBeTrimmed.add(trimCandidate); + } + } + final Set retainedVars = setMinus(variablesInPlan, canBeTrimmed); + if (!canBeTrimmed.isEmpty() && !(onlyIfNotDetermined && areVariablesDetermined(plan, retainedVars, canBeTrimmed, analyzer, false))) { + // TODO add smart ordering? + plan = planFactory.createSubPlan(new PProject(retainedVars), plan); + } + return plan; + } + + /** + * @return true iff a set of given variables functionally determine all visible variables in the subplan according to the subplan's constraints + * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; + * if false, user-provided soft dependencies are included as well, that are anticipated but not guaranteed by the storage mechanism; + * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance + * @since 1.5 + */ + public static boolean areAllVariablesDetermined(SubPlan plan, Collection determining, QueryAnalyzer analyzer, boolean strict) { + return areVariablesDetermined(plan, determining, plan.getVisibleVariables(), analyzer, strict); + } + + /** + * @return true iff one set of given variables functionally determine the other set according to the subplan's constraints + * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; + * if false, user-provided soft dependencies are included as well, that are anticipated but not guaranteed by the storage mechanism; + * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance + * @since 1.5 + */ + public static boolean areVariablesDetermined(SubPlan plan, Collection determining, Collection determined, + QueryAnalyzer analyzer, boolean strict) { + Map, Set> dependencies = analyzer.getFunctionalDependencies(plan.getAllEnforcedConstraints(), strict); + final Set closure = FunctionalDependencyHelper.closureOf(determining, dependencies); + final boolean isDetermined = closure.containsAll(determined); + return isDetermined; + } + + private static Set setMinus(Set a, Set b) { + Set difference = new HashSet(a); + difference.removeAll(b); + return difference; + } + + /** + * Finds an arbitrary constraint that is not enforced at the given plan. + * + * @param pSystem + * @param plan + * @return a PConstraint that is not enforced, if any, or null if all are enforced + */ + public static PConstraint getAnyUnenforcedConstraint(PBody pSystem, + SubPlan plan) { + Set allEnforcedConstraints = plan.getAllEnforcedConstraints(); + Set constraints = pSystem.getConstraints(); + for (PConstraint pConstraint : constraints) { + if (!allEnforcedConstraints.contains(pConstraint)) + return pConstraint; + } + return null; + } + + /** + * Skips the last few steps, if any, that are projections, so that a custom projection can be added instead. + * Useful for connecting body final plans into the production node. + * + * @since 2.1 + */ + public static SubPlan eliminateTrailingProjections(SubPlan plan) { + while (plan.getOperation() instanceof PProject) + plan = plan.getParentPlans().get(0); + return plan; + } + + /** + * Verifies whether all constraints are enforced and exported parameters are present. + * + * @param pSystem + * @param plan + * @throws ViatraQueryRuntimeException + */ + public static void finalCheck(final PBody pSystem, SubPlan plan, IQueryMetaContext context) { + PConstraint unenforcedConstraint = getAnyUnenforcedConstraint(pSystem, plan); + if (unenforcedConstraint != null) { + throw new QueryProcessingException( + "Pattern matcher construction terminated without successfully enforcing constraint {1}." + + " Could be caused if the value of some variables can not be deduced, e.g. by circularity of pattern constraints.", + new String[] { unenforcedConstraint.toString() }, "Could not enforce a pattern constraint", null); + } + for (ExportedParameter export : pSystem + .getConstraintsOfType(ExportedParameter.class)) { + if (!export.isReadyAt(plan, context)) { + throw new QueryProcessingException( + "Exported pattern parameter {1} could not be deduced during pattern matcher construction." + + " A pattern constraint is required to positively deduce its value.", + new String[] { export.getParameterName() }, "Could not calculate pattern parameter", + null); + } + } + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java new file mode 100644 index 00000000..40835f52 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/FunctionalDependencyHelper.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Adam Dudas, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.planning.helpers; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.util.Sets; + +/** + * Helper utility class for functional dependency analysis. + * + * Throughout this class attribute sets are represented as generic sets and functional dependencies as maps from + * attribute set (generic sets) to attribute set (generic sets) + * + * @author Adam Dudas + * + */ +public class FunctionalDependencyHelper { + + private FunctionalDependencyHelper() { + // Hiding constructor for utility class + } + + /** + * Get the closure of the specified attribute set relative to the specified functional dependencies. + * + * @param attributes + * The attributes to get the closure of. + * @param dependencies + * The functional dependencies of which the closure operation is relative to. + * @return The closure of the specified attribute set relative to the specified functional dependencies. + */ + public static Set closureOf(Collection attributes, Map, Set> dependencies) { + Set closureSet = new HashSet(); + + for (Set closureSet1 = new HashSet(attributes); closureSet.addAll(closureSet1);) { + closureSet1 = new HashSet(); + for (Entry, Set> dependency : dependencies.entrySet()) { + if (closureSet.containsAll(dependency.getKey())) + closureSet1.addAll(dependency.getValue()); + } + } + + return closureSet; + } + + /** + * @return true if the dependency from the left set to the right set is trivial + * @since 1.5 + */ + public static boolean isTrivial(Set left, Set right) { + return left.containsAll(right); + } + + /*** + * Returns the dependency set over attributes in {@link targetAttributes} that are implied by a given source dependency set. + *

Note: exponential in the size of the target attribute set. + *

Note: minimality of the returned dependency set is currently not guaranteed. + * @param originalDependencies all dependencies that are known to hold on a wider set of attributes + * @param targetAttributes the set of attributes we are interested in + * @since 1.5 + */ + public static Map, Set> projectDependencies(Map, Set> originalDependencies, Set targetAttributes) { + // only those attributes are considered as left-hand-side candidates that occur at least once in dependencies + Set leftCandidates = new HashSet(); + for (Entry, Set> dependency : originalDependencies.entrySet()) { + if (!isTrivial(dependency.getKey(), dependency.getValue())) // only if non-trivial + leftCandidates.addAll(Sets.intersection(dependency.getKey(), targetAttributes)); + } + + // Compute an initial list of nontrivial projected dependencies - it does not have to be minimal yet + Map, Set> initialDependencies = new HashMap, Set>(); + for (Set leftSet : Sets.powerSet(leftCandidates)) { + Set rightSet = Sets.intersection(closureOf(leftSet, originalDependencies), targetAttributes); + if (!isTrivial(leftSet, rightSet)) { + initialDependencies.put(leftSet, rightSet); + } + } + // Don't forget to include constants! + Set constants = Sets.intersection(closureOf(Collections.emptySet(), originalDependencies), targetAttributes); + if (! constants.isEmpty()) { + initialDependencies.put(Collections.emptySet(), constants); + } + + // Omit those dependencies where the LHS has superfluous attributes + Map, Set> solidDependencies = new HashMap, Set>(); + for (Entry, Set> dependency : initialDependencies.entrySet()) { + Set leftSet = dependency.getKey(); + Set rightSet = dependency.getValue(); + boolean solid = true; + for (A skipped : leftSet) { // what if we skip one attribute from the left set? + Set singleton = Collections.singleton(skipped); + Set candidate = Sets.difference(leftSet, singleton); + Set rightCandidate = initialDependencies.get(candidate); + if (rightCandidate != null) { + if (Sets.union(rightCandidate, singleton).containsAll(rightSet)) { + solid = false; + break; + } + } + } + if (solid) { + solidDependencies.put(leftSet, rightSet); + } + } + + // TODO perform proper minimization, + // see e.g. page 45 in http://www.cs.ubc.ca/~hkhosrav/db/slides/03.design%20theory.pdf + + return Collections.unmodifiableMap(solidDependencies); + } + + /** + * Adds a given dependency to a mutable accumulator. + * @since 1.5 + */ + public static void includeDependency(Map, Set> accumulator, Set left, Set right) { + Set accumulatorRights = accumulator.computeIfAbsent(left, l -> new HashSet<>()); + accumulatorRights.addAll(right); + } + + /** + * Adds all given dependencies to a mutable accumulator. + * @since 1.5 + */ + public static void includeDependencies(Map, Set> accumulator, Map, Set> additionalDependencies) { + for (Entry, Set> entry : additionalDependencies.entrySet()) { + includeDependency(accumulator, entry.getKey(), entry.getValue()); + } + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java new file mode 100644 index 00000000..b4f848a7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/StatisticsHelper.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.planning.helpers; + +import java.util.Optional; +import java.util.function.BiFunction; + +import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; +import tools.refinery.viatra.runtime.matchers.util.Accuracy; + +/** + * Helpers dealing with optionally present statistics information + * + * @author Gabor Bergmann + * @since 2.1 + * + */ +public class StatisticsHelper { + + private StatisticsHelper() { + // Hidden utility class constructor + } + + public static Optional estimateAverageBucketSize(TupleMask groupMask, Accuracy requiredAccuracy, + BiFunction> estimateCardinality) + { + if (groupMask.isIdentity()) return Optional.of(1.0); + + Accuracy numeratorAccuracy = requiredAccuracy; + Accuracy denominatorAccuracy = requiredAccuracy.reciprocal(); + TupleMask identityMask = TupleMask.identity(groupMask.sourceWidth); + + Optional totalCountEstimate = estimateCardinality.apply(identityMask, numeratorAccuracy); + Optional bucketCountEstimate = estimateCardinality.apply(groupMask, denominatorAccuracy); + + return totalCountEstimate.flatMap(matchCount -> + bucketCountEstimate.map(bucketCount -> + bucketCount == 0L ? 0L : ((double) matchCount) / bucketCount + )); + } + + public static Optional min(Optional a, Optional b) { + if (b.isPresent()) { + if (a.isPresent()) { + return Optional.of(Math.min(a.get(), b.get())); + } else return b; + } else return a; + } + public static Optional min(Optional a, double b) { + if (a.isPresent()) { + return Optional.of(Math.min(a.get(), b)); + } else return Optional.of(b); + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java new file mode 100644 index 00000000..926a591f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/helpers/TypeHelper.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.planning.helpers; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Queue; +import java.util.Set; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; + +/** + * @author Gabor Bergmann + * @author Tamas Szabo + */ +public class TypeHelper { + + private TypeHelper() { + // Hiding constructor for utility class + } + + /** + * Collects the type constraints for the specified collection of variables. The type constraints consist of the + * constraints directly enforced on the variable itself, plus all of those that the given variable is unified with + * through equalities. + * + * @param variables + * the variables in question + * @param constraints + * the constraints in the pattern body + * @param context + * the query meta context + * @return the mapping from variable to set of type constraints + * @since 1.6 + */ + public static Map> inferUnaryTypesFor(Iterable variables, + Set constraints, IQueryMetaContext context) { + Map> typeMap = TypeHelper.inferUnaryTypes(constraints, context); + return inferUnaryTypesFor(variables, typeMap); + } + + /** + * Collects the type constraints for the specified collection of variables. The type constraints consist of the + * constraints directly enforced on the variable itself, plus all of those that the given variable is unified with + * through equalities. + * + * The method accepts a type map which is the result of the basic type inference from the + * {@link TypeHelper.inferUnaryTypes} method. The purpose of this method is that the type map can be reused across + * several calls to this method. + * + * @param variables + * the variables in question + * @param typeMap + * the type map of inference results + * @return the mapping from variable to set of type constraints + * @since 1.6 + */ + public static Map> inferUnaryTypesFor(Iterable variables, + Map> typeMap) { + Map> result = new HashMap>(); + + for (PVariable original : variables) { + // it can happen that the variable was unified into an other one due to equalities + Set keys = new HashSet(); + PVariable current = original; + + while (current != null) { + Set judgements = typeMap.get(current); + if (judgements != null) { + for (TypeJudgement judgement : judgements) { + keys.add(judgement.getInputKey()); + } + } + current = current.getDirectUnifiedInto(); + } + + result.put(original, keys); + } + + return result; + } + + /** + * Infers unary type information for variables, based on the given constraints. + * + * Subsumptions are not taken into account. + * + * @param constraints + * the set of constraints to extract type info from + */ + public static Map> inferUnaryTypes(Set constraints, + IQueryMetaContext context) { + Set equivalentJudgements = getDirectJudgements(constraints, context); + Set impliedJudgements = typeClosure(equivalentJudgements, context); + + Map> results = new HashMap>(); + for (TypeJudgement typeJudgement : impliedJudgements) { + final IInputKey inputKey = typeJudgement.getInputKey(); + if (inputKey.getArity() == 1) { + PVariable variable = (PVariable) typeJudgement.getVariablesTuple().get(0); + Set inferredTypes = results.computeIfAbsent(variable, v -> new HashSet<>()); + inferredTypes.add(typeJudgement); + } + } + return results; + } + + /** + * Gets direct judgements reported by constraints. No closure is applied yet. + */ + public static Set getDirectJudgements(Set constraints, IQueryMetaContext context) { + Set equivalentJudgements = new HashSet(); + for (PConstraint pConstraint : constraints) { + if (pConstraint instanceof ITypeInfoProviderConstraint) { + equivalentJudgements.addAll(((ITypeInfoProviderConstraint) pConstraint).getImpliedJudgements(context)); + } + } + return equivalentJudgements; + } + + /** + * Calculates the closure of a set of type judgements, with respect to supertyping. + * + * @return the set of all type judgements in typesToClose and all their direct and indirect supertypes + */ + public static Set typeClosure(Set typesToClose, IQueryMetaContext context) { + return typeClosure(Collections. emptySet(), typesToClose, context); + } + + /** + * Calculates the closure of a set of type judgements (with respect to supertyping), where the closure has been + * calculated before for a given base set, but not for a separate delta set. + *

+ * Precondition: the set (typesToClose MINUS delta) is already closed w.r.t. supertyping. + * + * @return the set of all type judgements in typesToClose and all their direct and indirect supertypes + * @since 1.6 + */ + public static Set typeClosure(Set preclosedBaseSet, Set delta, + IQueryMetaContext context) { + Queue queue = delta.stream().filter(input -> !preclosedBaseSet.contains(input)).collect(Collectors.toCollection(LinkedList::new)); + if (queue.isEmpty()) + return preclosedBaseSet; + + Set closure = new HashSet(preclosedBaseSet); + + Map> conditionalImplications = new HashMap<>(); + for (TypeJudgement typeJudgement : closure) { + conditionalImplications.putAll(typeJudgement.getConditionalImpliedJudgements(context)); + } + + do { + TypeJudgement deltaType = queue.poll(); + if (closure.add(deltaType)) { + // direct implications + queue.addAll(deltaType.getDirectlyImpliedJudgements(context)); + + // conditional implications, source key processed before, this is the condition key + final Set implicationSet = conditionalImplications.get(deltaType); + if (implicationSet != null) { + queue.addAll(implicationSet); + } + + // conditional implications, this is the source key + Map> deltaConditionalImplications = deltaType + .getConditionalImpliedJudgements(context); + for (Entry> entry : deltaConditionalImplications.entrySet()) { + if (closure.contains(entry.getKey())) { + // condition processed before + queue.addAll(entry.getValue()); + } else { + // condition not processed yet + conditionalImplications.computeIfAbsent(entry.getKey(), key -> new HashSet<>()) + .addAll(entry.getValue()); + } + } + } + } while (!queue.isEmpty()); + + return closure; + } + + /** + * Calculates a remainder set of types from a larger set, that are not subsumed by a given set of subsuming types. + * + * @param subsumableTypes + * a set of types from which some may be implied by the subsuming types + * @param subsumingTypes + * a set of types that may imply some of the subsuming types + * @return the collection of types in subsumableTypes that are NOT identical to or supertypes of any type in + * subsumingTypes. + */ + public static Set subsumeTypes(Set subsumableTypes, Set subsumingTypes, + IQueryMetaContext context) { + Set closure = typeClosure(subsumingTypes, context); + Set subsumed = new HashSet(subsumableTypes); + subsumed.removeAll(closure); + return subsumed; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java new file mode 100644 index 00000000..2c285b54 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PApply.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.planning.operations; + +import java.util.Collections; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * Represents a constraint application on a single parent SubPlan. + *

Either a "selection" filter operation according to a deferred PConstraint (or transform in case of eval/aggregate), or + * alternatively a shorthand for PJoin + a PEnumerate on the right input for an enumerable PConstraint. + * + *

WARNING: if there are coinciding variables in the variable tuple of the enumerable constraint, + * it is the responsibility of the compiler to check them for equality. + * + * @author Bergmann Gabor + * + */ +public class PApply extends POperation { + + private PConstraint pConstraint; + + public PApply(PConstraint pConstraint) { + super(); + this.pConstraint = pConstraint; + } + public PConstraint getPConstraint() { + return pConstraint; + } + + @Override + public String getShortName() { + return String.format("APPLY_%s", pConstraint.toString()); + } + + @Override + public Set getDeltaConstraints() { + return Collections.singleton(pConstraint); + } + + @Override + public int numParentSubPlans() { + return 1; + } + + @Override + public void checkConsistency(SubPlan subPlan) { + super.checkConsistency(subPlan); + for (SubPlan parentPlan : subPlan.getParentPlans()) + Preconditions.checkArgument(!parentPlan.getAllEnforcedConstraints().contains(pConstraint), + "Double-checking constraint %s", pConstraint); + // TODO obtain context? + //if (pConstraint instanceof DeferredPConstraint) + // Preconditions.checkArgument(((DeferredPConstraint) pConstraint).isReadyAt(subPlan, context)) + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime + * result + + ((pConstraint == null) ? 0 : pConstraint + .hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof PApply)) + return false; + PApply other = (PApply) obj; + if (pConstraint == null) { + if (other.pConstraint != null) + return false; + } else if (!pConstraint.equals(other.pConstraint)) + return false; + return true; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java new file mode 100644 index 00000000..a975d50c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PEnumerate.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.planning.operations; + +import java.util.Collections; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; + +/** + * Represents a base relation defined by the instance set of an enumerable PConstraint; there are no parent SubPlans. + * + *

WARNING: if there are coinciding variables in the variable tuple of the enumerable constraint, + * it is the responsibility of the compiler to check them for equality. + * @author Bergmann Gabor + * + */ +public class PEnumerate extends POperation { + + EnumerablePConstraint enumerablePConstraint; + + public PEnumerate(EnumerablePConstraint enumerablePConstraint) { + super(); + this.enumerablePConstraint = enumerablePConstraint; + } + public EnumerablePConstraint getEnumerablePConstraint() { + return enumerablePConstraint; + } + + @Override + public Set getDeltaConstraints() { + return Collections.singleton(enumerablePConstraint); + } + @Override + public int numParentSubPlans() { + return 0; + } + @Override + public String getShortName() { + return enumerablePConstraint.toString(); + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime + * result + + ((enumerablePConstraint == null) ? 0 : enumerablePConstraint + .hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof PEnumerate)) + return false; + PEnumerate other = (PEnumerate) obj; + if (enumerablePConstraint == null) { + if (other.enumerablePConstraint != null) + return false; + } else if (!enumerablePConstraint.equals(other.enumerablePConstraint)) + return false; + return true; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java new file mode 100644 index 00000000..10e0a85a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PJoin.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.planning.operations; + +import java.util.Collections; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; + +/** + * Represents a natural join of two parent SubPlans. + * @author Bergmann Gabor + * + */ +public class PJoin extends POperation { + +// // TODO leave here? is this a problem in equivalnece checking? +// private Set onVariables; + + public PJoin(/*Set onVariables*/) { + super(); + //this.onVariables = new HashSet(onVariables); + } +// public Set getOnVariables() { +// return onVariables; +// } + + @Override + public Set getDeltaConstraints() { + return Collections.emptySet(); + } + @Override + public int numParentSubPlans() { + return 2; + } + + @Override + public String getShortName() { + return "JOIN"; //String.format("JOIN_{%s}", Joiner.on(",").join(onVariables)); + } + + @Override + public int hashCode() { + return getClass().hashCode(); + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof PJoin)) + return false; + return true; + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java new file mode 100644 index 00000000..e71cf217 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/POperation.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.planning.operations; + +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * Abstract superclass for representing a high-level query evaluation operation. + * + *

Subclasses correspond to various POperations modeled after relational algebra. + * + * @author Bergmann Gabor + * + */ +public abstract class POperation { + + /** + * Newly enforced constraints + */ + public abstract Set getDeltaConstraints(); + + public abstract String getShortName(); + + /** + * @return the number of SubPlans that must be specified as parents + */ + public abstract int numParentSubPlans(); + + /** + * Checks whether this constraint can be properly applied at the given SubPlan. + */ + public void checkConsistency(SubPlan subPlan) { + Preconditions.checkArgument(this == subPlan.getOperation(), "POperation misalignment"); + Preconditions.checkArgument(subPlan.getParentPlans().size() == numParentSubPlans(), "Incorrect number of parent SubPlans"); + } + + @Override + public String toString() { + return getShortName(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java new file mode 100644 index 00000000..d0539b2c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PProject.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.planning.operations; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * Represents a projection of a single parent SubPlan onto a limited set of variables. + *

May optionally prescribe an ordering of variables (List, as opposed to Set). + * + * @author Bergmann Gabor + * + */ +public class PProject extends POperation { + + private Collection toVariables; + private boolean ordered; + + + public PProject(Set toVariables) { + super(); + this.toVariables = toVariables; + this.ordered = false; + } + public PProject(List toVariables) { + super(); + this.toVariables = toVariables; + this.ordered = true; + } + + public Collection getToVariables() { + return toVariables; + } + public boolean isOrdered() { + return ordered; + } + + @Override + public Set getDeltaConstraints() { + return Collections.emptySet(); + } + @Override + public int numParentSubPlans() { + return 1; + } + @Override + public void checkConsistency(SubPlan subPlan) { + super.checkConsistency(subPlan); + final SubPlan parentPlan = subPlan.getParentPlans().get(0); + + Preconditions.checkArgument(parentPlan.getVisibleVariables().containsAll(toVariables), + () -> toVariables.stream() + .filter(input -> !parentPlan.getVisibleVariables().contains(input)).map(PVariable::getName) + .collect(Collectors.joining(",", "Variables missing from project: ", ""))); + } + + @Override + public String getShortName() { + return String.format("PROJECT%s_{%s}", ordered? "!" : "", + toVariables.stream().map(PVariable::getName).collect(Collectors.joining(","))); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (ordered ? 1231 : 1237); + result = prime * result + + ((toVariables == null) ? 0 : toVariables.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof PProject)) + return false; + PProject other = (PProject) obj; + if (ordered != other.ordered) + return false; + if (toVariables == null) { + if (other.toVariables != null) + return false; + } else if (!toVariables.equals(other.toVariables)) + return false; + return true; + } + + + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java new file mode 100644 index 00000000..9e6ea10e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/planning/operations/PStart.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.planning.operations; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; + +/** + * No constraints, and no parent SubPlan, just a (possibly empty) set of a priori known (input) variables. Satisfied by a single tuple. + * + *

Can also be used without a priori variables, + * e.g. as a "virtual parent" in extreme cases, + * such as pattern foo(Bar) = {Bar = eval (3*4)} + * + * @author Bergmann Gabor + * + */ +public class PStart extends POperation { + + private Set aPrioriVariables; + + + public PStart(Set aPrioriVariables) { + super(); + this.aPrioriVariables = aPrioriVariables; + } + public PStart(PVariable... aPrioriVariables) { + this(new HashSet(Arrays.asList(aPrioriVariables))); + } + public Set getAPrioriVariables() { + return aPrioriVariables; + } + + @Override + public String getShortName() { + return aPrioriVariables.stream().map(PVariable::getName).collect(Collectors.joining(",", "START_{", "}")); + } + @Override + public int numParentSubPlans() { + return 0; + } + + @Override + public Set getDeltaConstraints() { + return Collections.emptySet(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime + * result + + ((aPrioriVariables == null) ? 0 : aPrioriVariables.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof PStart)) + return false; + PStart other = (PStart) obj; + if (aPrioriVariables == null) { + if (other.aPrioriVariables != null) + return false; + } else if (!aPrioriVariables.equals(other.aPrioriVariables)) + return false; + return true; + } + + + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java new file mode 100644 index 00000000..eda4aa25 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/BasePConstraint.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * @author Gabor Bergmann + * + */ +public abstract class BasePConstraint implements PConstraint { + + + protected PBody pBody; + private final Set affectedVariables; + + + private final int sequentialID = nextID.getAndIncrement(); + + // Use a static atomic integer to avoid race conditions when creating new constraints. + private static AtomicInteger nextID = new AtomicInteger(0); + + public BasePConstraint(PBody pBody, Set affectedVariables) { + super(); + this.pBody = pBody; + this.affectedVariables = new HashSet(affectedVariables); + + for (PVariable pVariable : affectedVariables) { + pVariable.refer(this); + } + pBody.registerConstraint(this); + } + + @Override + public String toString() { + return "PC[" + getClass().getSimpleName() + ":" + toStringRest() + "]"; + } + + protected abstract String toStringRest(); + + @Override + public Set getAffectedVariables() { + return affectedVariables; + } + + @Override + public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { + return Collections.emptyMap(); + } + + @Override + public void replaceVariable(PVariable obsolete, PVariable replacement) { + pBody.checkMutability(); + if (affectedVariables.remove(obsolete)) { + affectedVariables.add(replacement); + obsolete.unrefer(this); + replacement.refer(this); + doReplaceVariable(obsolete, replacement); + } + } + + protected abstract void doReplaceVariable(PVariable obsolete, PVariable replacement); + + @Override + public void delete() { + pBody.checkMutability(); + for (PVariable pVariable : affectedVariables) { + pVariable.unrefer(this); + } + pBody.unregisterConstraint(this); + } + + @Override + public void checkSanity() { + } + + /** + * For backwards compatibility. Equivalent to {@link #getBody()} + */ + public PBody getPSystem() { + return pBody; + } + /** + * @since 2.1 + */ + @Override + public PBody getBody() { + return pBody; + } + + @Override + public int getMonotonousID() { + return sequentialID; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java new file mode 100644 index 00000000..d2bf088c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/DeferredPConstraint.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; + +/** + * Any constraint that can only be checked on certain SubPlans (e.g. those plans that already contain some variables). + * + * @author Gabor Bergmann + * + */ +public abstract class DeferredPConstraint extends BasePConstraint { + + public DeferredPConstraint(PBody pBody, Set affectedVariables) { + super(pBody, affectedVariables); + } + + public abstract boolean isReadyAt(SubPlan plan, IQueryMetaContext context); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java new file mode 100644 index 00000000..9129aa47 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/EnumerablePConstraint.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * A constraint for which all satisfying tuples of variable values can be enumerated at any point during run-time. + * + * @author Gabor Bergmann + * + */ +public abstract class EnumerablePConstraint extends BasePConstraint { + protected Tuple variablesTuple; + + protected EnumerablePConstraint(PBody pBody, Tuple variablesTuple) { + super(pBody, variablesTuple. getDistinctElements()); + this.variablesTuple = variablesTuple; + } + + @Override + public void doReplaceVariable(PVariable obsolete, PVariable replacement) { + variablesTuple = variablesTuple.replaceAll(obsolete, replacement); + } + + @Override + protected String toStringRest() { + String stringRestRest = toStringRestRest(); + String tupleString = "@" + variablesTuple.toString(); + return stringRestRest == null ? tupleString : ":" + stringRestRest + tupleString; + } + + protected String toStringRestRest() { + return null; + } + + public Tuple getVariablesTuple() { + return variablesTuple; + } + + @Override + public Set getDeducedVariables() { + return getAffectedVariables(); + } + + public PVariable getVariableInTuple(int index) { + return (PVariable) this.variablesTuple.get(index); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java new file mode 100644 index 00000000..686999f7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IExpressionEvaluator.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem; + +/** + * An expression evaluator is used to execute arbitrary Java code during pattern matching. In order to include the + * evaluation in the planning seemlessly it is expected from the evaluator implementors to report all used PVariables by + * name. + * + * @author Zoltan Ujhelyi + * + */ +public interface IExpressionEvaluator { + + /** + * A textual description of the expression. Used only for debug purposes, but must not be null. + */ + String getShortDescription(); + + /** + * All input parameter names should be reported correctly. + */ + Iterable getInputParameterNames(); + + /** + * The expression evaluator code + * + * @param provider + * the value provider is an engine-specific way of reading internal variable tuples to evaluate the + * expression with + * @return the result of the expression: in case of predicate evaluation the return value must be true or false; + * otherwise the result can be an arbitrary object. No null values should be returned. + * @throws Exception + */ + Object evaluateExpression(IValueProvider provider) throws Exception; +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java new file mode 100644 index 00000000..8f647c64 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IMultiQueryReference.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2010-2022, Tamas Szabo, GitHub + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.Collection; + +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * A {@link PConstraint} that implements this interface refers to a list of PQueries. + * + * @author Tamas Szabo + * @since 2.8 + * + */ +public interface IMultiQueryReference { + + Collection getReferredQueries(); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java new file mode 100644 index 00000000..9ee05b39 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IQueryReference.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.Collections; +import java.util.List; + +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * A {@link PConstraint} that implements this interface refers to a {@link PQuery}. + * + * @author Zoltan Ujhelyi + * + */ +public interface IQueryReference extends IMultiQueryReference { + + PQuery getReferredQuery(); + + /** + * @since 2.8 + */ + @Override + default List getReferredQueries() { + return Collections.singletonList(getReferredQuery()); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java new file mode 100644 index 00000000..e4c396d8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IRelationEvaluator.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2010-2022, Tamas Szabo, GitHub + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * Implementations of this interface take an arbitrary number of input relations with their contents and compute the + * tuples of a single output relation. + * + * @author Tamas Szabo + * @since 2.8 + * + */ +public interface IRelationEvaluator { + + /** + * A textual description of the evaluator. Used only for debug purposes, but must not be null. + */ + String getShortDescription(); + + /** + * The relation evaluator code. For performance reasons, it is expected that the returned set is a mutable + * collection, and the caller must be allowed to actually perform mutations! + */ + Set evaluateRelation(List> inputs) throws Exception; + + /** + * For each input relation that this evaluator requires, this method returns the expected arities of the relations in order. + */ + List getInputArities(); + + /** + * Returns the arity of the output relation that this evaluator computes. + */ + int getOutputArity(); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java new file mode 100644 index 00000000..b72035a8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeConstraint.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +import java.util.Set; + +/** + * Common superinterface of enumerable and deferred type constraints. + * @author Bergmann Gabor + * + */ +public interface ITypeConstraint extends ITypeInfoProviderConstraint { + + public abstract TypeJudgement getEquivalentJudgement(); + + /** + * Static internal utility class for implementations of {@link ITypeConstraint}s. + * @author Bergmann Gabor + */ + public static class TypeConstraintUtil { + + private TypeConstraintUtil() { + // Hiding constructor for utility class + } + + public static Map, Set> getFunctionalDependencies(IQueryMetaContext context, IInputKey inputKey, Tuple variablesTuple) { + final Map, Set> result = new HashMap, Set>(); + + Set, Set>> dependencies = context.getFunctionalDependencies(inputKey).entrySet(); + for (Entry, Set> dependency : dependencies) { + result.put( + transcribeVariables(dependency.getKey(), variablesTuple), + transcribeVariables(dependency.getValue(), variablesTuple) + ); + } + + return result; + } + + private static Set transcribeVariables(Set indices, Tuple variablesTuple) { + Set result = new HashSet(); + for (Integer index : indices) { + result.add((PVariable) variablesTuple.get(index)); + } + return result; + } + + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java new file mode 100644 index 00000000..ff127d38 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/ITypeInfoProviderConstraint.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; + +/** + * @author Gabor Bergmann + * + */ +public interface ITypeInfoProviderConstraint extends PConstraint { + + /** + * Returns type information implied by this constraint. + * + */ + public Set getImpliedJudgements(IQueryMetaContext context); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java new file mode 100644 index 00000000..d959adc4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/IValueProvider.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem; + +/** + * Helper interface to get values from a tuple of variables. All pattern matching engines are expected to implement this + * to handle their internal structures. + * + * @author Zoltan Ujhelyi + * + */ +public interface IValueProvider { + + /** + * Returns the value of the selected variable + * @param variableName + * @return the value of the variable; never null + * @throws IllegalArgumentException if the variable is not defined + */ + Object getValue(String variableName); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java new file mode 100644 index 00000000..a82d12ec --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/InitializablePQuery.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PProblem; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * Adds extra methods to the PQuery interface to initialize its contents. + * + * @author Zoltan Ujhelyi + * + */ +public interface InitializablePQuery extends PQuery { + + /** + * Sets the query status. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED uninitialized}. + * + * @param status the new status + */ + void setStatus(PQueryStatus status); + + /** + * Adds a detected error. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED uninitialized}. + * + * @param problem the new problem + */ + void addError(PProblem problem); + + /** + * Sets up the bodies of the pattern. Only applicable if the pattern is still {@link PQueryStatus#UNINITIALIZED + * uninitialized}. + * + * @param bodies + * @throws ViatraQueryRuntimeException + */ + void initializeBodies(Set bodies); + + /** + * Adds an annotation to the specification. Only applicable if the pattern is still + * {@link PQueryStatus#UNINITIALIZED uninitialized}. + * + * @param annotation + */ + void addAnnotation(PAnnotation annotation); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java new file mode 100644 index 00000000..91eea817 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/KeyedEnumerablePConstraint.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * @author Gabor Bergmann + * + */ +public abstract class KeyedEnumerablePConstraint extends EnumerablePConstraint { + + protected KeyType supplierKey; + + public KeyedEnumerablePConstraint(PBody pBody, Tuple variablesTuple, + KeyType supplierKey) { + super(pBody, variablesTuple); + this.supplierKey = supplierKey; + } + + @Override + protected String toStringRestRest() { + return supplierKey == null ? "$any(null)" : keyToString(); + } + + protected abstract String keyToString(); + + public KeyType getSupplierKey() { + return supplierKey; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java new file mode 100644 index 00000000..c38dc23a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PBody.java @@ -0,0 +1,289 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.ConstantValue; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * A set of constraints representing a pattern body + * + * @author Gabor Bergmann + * + */ +public class PBody implements PTraceable { + + public static final String VIRTUAL_VARIABLE_PREFIX = ".virtual"; + private static final String VIRTUAL_VARIABLE_PATTERN = VIRTUAL_VARIABLE_PREFIX + "{%d}"; + + private PQuery query; + + /** + * If null, then parent query status is reused + */ + private PQueryStatus status = PQueryStatus.UNINITIALIZED; + + private Set allVariables; + private Set uniqueVariables; + private List symbolicParameters; + private Map variablesByName; + private Set constraints; + private int nextVirtualNodeID; + private PDisjunction containerDisjunction; + + public PBody(PQuery query) { + super(); + this.query = query; + allVariables = new LinkedHashSet<>(); + uniqueVariables = new LinkedHashSet<>(); + variablesByName = new HashMap<>(); + constraints = new LinkedHashSet<>(); + } + + /** + * @return whether the submission of the new variable was successful + */ + private boolean addVariable(PVariable var) { + checkMutability(); + Object name = var.getName(); + if (!variablesByName.containsKey(name)) { + allVariables.add(var); + if (var.isUnique()) + uniqueVariables.add(var); + variablesByName.put(name, var); + return true; + } else { + return false; + } + } + + /** + * Use this method to add a newly created constraint to the pSystem. + * + * @return whether the submission of the new constraint was successful + */ + boolean registerConstraint(PConstraint constraint) { + checkMutability(); + return constraints.add(constraint); + } + + /** + * Use this method to remove an obsolete constraint from the pSystem. + * + * @return whether the removal of the constraint was successful + */ + boolean unregisterConstraint(PConstraint constraint) { + checkMutability(); + return constraints.remove(constraint); + } + + @SuppressWarnings("unchecked") + public Set getConstraintsOfType(Class constraintClass) { + Set result = new HashSet(); + for (PConstraint pConstraint : constraints) { + if (constraintClass.isInstance(pConstraint)) + result.add((ConstraintType) pConstraint); + } + return result; + } + + public PVariable newVirtualVariable() { + checkMutability(); + String name; + do { + + name = String.format(VIRTUAL_VARIABLE_PATTERN, nextVirtualNodeID++); + } while (variablesByName.containsKey(name)); + PVariable var = new PVariable(this, name, true); + addVariable(var); + return var; + } + + public PVariable newVirtualVariable(String name) { + checkMutability(); + Preconditions.checkArgument(!variablesByName.containsKey(name), "ID %s already used for a virtual variable", name); + PVariable var = new PVariable(this, name, true); + addVariable(var); + return var; + } + + public PVariable newConstantVariable(Object value) { + checkMutability(); + PVariable virtual = newVirtualVariable(); + new ConstantValue(this, virtual, value); + return virtual; + } + + public Set getAllVariables() { + return allVariables; + } + + public Set getUniqueVariables() { + return uniqueVariables; + } + + private PVariable getVariableByName(Object name) { + return variablesByName.get(name).getUnifiedIntoRoot(); + } + + /** + * Find a PVariable by name + * + * @param name + * @return the found variable + * @throws IllegalArgumentException + * if no PVariable is found with the selected name + */ + public PVariable getVariableByNameChecked(Object name) { + if (!variablesByName.containsKey(name)) + throw new IllegalArgumentException(String.format("Cannot find PVariable %s", name)); + return getVariableByName(name); + } + + /** + * Finds and returns a PVariable by name. If no PVariable exists with the name in the body, a new one is created. If + * the name of the variable starts with {@value #VIRTUAL_VARIABLE_PREFIX}, the created variable will be considered + * virtual. + * + * @param name + * @return a PVariable with the selected name; never null + */ + public PVariable getOrCreateVariableByName(String name) { + checkMutability(); + if (!variablesByName.containsKey(name)) { + addVariable(new PVariable(this, name, name.startsWith(VIRTUAL_VARIABLE_PREFIX))); + } + return getVariableByName(name); + } + + public Set getConstraints() { + return constraints; + } + + public PQuery getPattern() { + return query; + } + + void noLongerUnique(PVariable pVariable) { + assert (!pVariable.isUnique()); + uniqueVariables.remove(pVariable); + } + + /** + * Returns the symbolic parameters of the body.

+ * + *

+ * Warning: if two PVariables are unified, the returned list changes. If you want to have a stable + * version, consider using {@link #getSymbolicParameters()}. + * + * @return a non-null, but possibly empty list + */ + public List getSymbolicParameterVariables() { + return getSymbolicParameters().stream().map(ExportedParameter::getParameterVariable) + .collect(Collectors.toList()); + } + + /** + * Returns the exported parameter constraints of the body. + * + * @return a non-null, but possibly empty list + */ + public List getSymbolicParameters() { + if (symbolicParameters == null) + symbolicParameters = new ArrayList<>(); + return symbolicParameters; + } + + /** + * Sets the exported parameter constraints of the body, if this instance is mutable. + * @param symbolicParameters the new value + */ + public void setSymbolicParameters(List symbolicParameters) { + checkMutability(); + this.symbolicParameters = new ArrayList<>(symbolicParameters); + } + + /** + * Sets a specific status for the body. If set, the parent PQuery status will not be checked; if set to null, its corresponding PQuery + * status is checked for mutability. + * + * @param status + * the status to set + */ + public void setStatus(PQueryStatus status) { + this.status = status; + } + + public boolean isMutable() { + if (status == null) { + return query.isMutable(); + } else { + return status.equals(PQueryStatus.UNINITIALIZED); + } + } + + void checkMutability() { + if (status == null) { + query.checkMutability(); + } else { + Preconditions.checkState(status.equals(PQueryStatus.UNINITIALIZED), "Initialized queries are not mutable"); + } + } + + /** + * Returns the disjunction the body is contained with. This disjunction may either be the + * {@link PQuery#getDisjunctBodies() canonical disjunction of the corresponding query} or something equivalent. + * + * @return the container disjunction of the body. Can be null if body is not in a disjunction yet. + */ + public PDisjunction getContainerDisjunction() { + return containerDisjunction; + } + + /** + * @param containerDisjunction the containerDisjunction to set + */ + public void setContainerDisjunction(PDisjunction containerDisjunction) { + Preconditions.checkArgument(query.equals(containerDisjunction.getQuery()), "Disjunction of pattern %s incompatible with body %s", containerDisjunction.getQuery().getFullyQualifiedName(), query.getFullyQualifiedName()); + Preconditions.checkState(this.containerDisjunction == null, "Disjunction is already set."); + this.containerDisjunction = containerDisjunction; + } + + /** + * All unary input keys directly prescribed by constraints, grouped by variable. + *

to supertype inference or subsumption applied at this point. + */ + public Map> getAllUnaryTypeRestrictions(IQueryMetaContext context) { + Map> currentRestrictions = allUnaryTypeRestrictions.get(context); + if (currentRestrictions == null) { + currentRestrictions = TypeHelper.inferUnaryTypes(getConstraints(), context); + allUnaryTypeRestrictions.put(context, currentRestrictions); + } + return currentRestrictions; + } + private WeakHashMap>> allUnaryTypeRestrictions = new WeakHashMap>>(); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java new file mode 100644 index 00000000..ae2c4632 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PConstraint.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.Comparator; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.analysis.QueryAnalyzer; + +/** + * @author Gabor Bergmann + * + */ +public interface PConstraint extends PTraceable { + + /** + * @since 2.1 + * @return the query body this constraint belongs to + */ + public PBody getBody(); + + /** + * All variables affected by this constraint. + */ + public Set getAffectedVariables(); + + /** + * The set of variables whose potential values can be enumerated (once all non-deduced variables have known values). + */ + public Set getDeducedVariables(); + + /** + * A (preferably minimal) cover of known functional dependencies between variables. + * @noreference Use {@link QueryAnalyzer} instead to properly handle dependencies of pattern calls. + * @return non-trivial functional dependencies in the form of {variables} --> {variables}, where dependencies with the same lhs are unified. + */ + public Map,Set> getFunctionalDependencies(IQueryMetaContext context); + + public void replaceVariable(PVariable obsolete, PVariable replacement); + + public void delete(); + + public void checkSanity(); + + /** + * Returns an integer ID that is guaranteed to increase strictly monotonously for constraints within a pBody. + */ + public abstract int getMonotonousID(); + + + /** + * A comparator that orders constraints by their {@link #getMonotonousID() monotonous identifiers}. Should only used + * for tiebreaking in other comparators. + * + * @since 2.0 + */ + public static final Comparator COMPARE_BY_MONOTONOUS_ID = (arg0, arg1) -> arg0.getMonotonousID() - arg1.getMonotonousID(); + + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java new file mode 100644 index 00000000..f0241a9c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PTraceable.java @@ -0,0 +1,16 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Dénes Harmath, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem; + +/** + * Marker interface for PSystem elements that can be traced. + * @since 1.6 + */ +public interface PTraceable { +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java new file mode 100644 index 00000000..b6ea4861 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/PVariable.java @@ -0,0 +1,203 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.HashSet; +import java.util.Set; + +/** + * @author Gabor Bergmann + * + */ +public class PVariable { + private PBody pBody; + /** + * The name of the pattern variable. This is the unique key of the pattern node. + */ + private String name; + /** + * virtual pVariables are nodes that do not correspond to actual pattern variables; they represent constants or Term + * substitutes + */ + private boolean virtual; + + /** + * Set of constraints that mention this variable + */ + private Set referringConstraints; + + /** + * Determines whether there are any constraints that can deduce this variable + */ + private Boolean deducable; + + /** + * Another PVariable this variable has been unified into. Please use the other variable instead of this. Null iff + * this is still a first-class variable. + */ + private PVariable unifiedInto; + + PVariable(PBody pBody, String name) { + this(pBody, name, false); + } + + PVariable(PBody pBody, String name, boolean virtual) { + super(); + this.pBody = pBody; + this.name = name; + this.virtual = virtual; + // this.exportedParameter = false; + this.referringConstraints = new HashSet(); + this.unifiedInto = null; + this.deducable = false; + } + + /** + * Replaces this variable with a given other, resulting in their unification. This variable will no longer be + * unique. + * + * @param replacement + */ + public void unifyInto(PVariable replacement) { + pBody.checkMutability(); + replacementCheck(); + replacement = replacement.getUnifiedIntoRoot(); + + if (this.equals(replacement)) + return; + + if (!this.isVirtual() && replacement.isVirtual()) { + replacement.unifyInto(this); + } else { + // replacement.referringConstraints.addAll(this.referringConstraints); + // replacement.exportedParameter |= this.exportedParameter; + replacement.virtual &= this.virtual; + if (replacement.deducable != null && this.deducable != null) + replacement.deducable |= this.deducable; + else + replacement.deducable = null; + Set snapshotConstraints = // avoid ConcurrentModificationX + new HashSet(this.referringConstraints); + for (PConstraint constraint : snapshotConstraints) { + constraint.replaceVariable(this, replacement); + } + // replacementCheck() will fail from this point + this.unifiedInto = replacement; + pBody.noLongerUnique(this); + } + } + + /** + * Determines whether there are any constraints that can deduce this variable + */ + public boolean isDeducable() { + replacementCheck(); + if (deducable == null) { + deducable = false; + for (PConstraint pConstraint : getReferringConstraints()) { + if (pConstraint.getDeducedVariables().contains(this)) { + deducable = true; + break; + } + } + } + return deducable; + } + + /** + * Register that this variable is referred by the given constraint. + * + * @param constraint + */ + public void refer(PConstraint constraint) { + pBody.checkMutability(); + replacementCheck(); + deducable = null; + referringConstraints.add(constraint); + } + + /** + * Register that this variable is no longer referred by the given constraint. + * + * @param constraint + */ + public void unrefer(PConstraint constraint) { + pBody.checkMutability(); + replacementCheck(); + deducable = null; + referringConstraints.remove(constraint); + } + + /** + * @return the name of the pattern variable. This is the unique key of the pattern node. + */ + public String getName() { + replacementCheck(); + return name; + } + + /** + * @return the virtual + */ + public boolean isVirtual() { + replacementCheck(); + return virtual; + } + + /** + * @return the referringConstraints + */ + public Set getReferringConstraints() { + replacementCheck(); + return referringConstraints; + } + + @SuppressWarnings("unchecked") + public Set getReferringConstraintsOfType(Class constraintClass) { + replacementCheck(); + Set result = new HashSet(); + for (PConstraint pConstraint : referringConstraints) { + if (constraintClass.isInstance(pConstraint)) + result.add((ConstraintType) pConstraint); + } + return result; + } + + @Override + public String toString() { + // replacementCheck(); + return name;// + ":PatternNode"; + } + + public PVariable getDirectUnifiedInto() { + return unifiedInto; + } + + public PVariable getUnifiedIntoRoot() { + PVariable nextUnified = unifiedInto; + PVariable oldUnifiedInto = this; + while (nextUnified != null) { + oldUnifiedInto = nextUnified; + nextUnified = oldUnifiedInto.getDirectUnifiedInto(); + } + return oldUnifiedInto; // unifiedInto; + } + + public boolean isUnique() { + return unifiedInto == null; + } + + private void replacementCheck() { + if (unifiedInto != null) + throw new IllegalStateException("Illegal usage of variable " + name + " which has been replaced with " + + unifiedInto.name); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java new file mode 100644 index 00000000..4447b225 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/TypeJudgement.java @@ -0,0 +1,153 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.context.InputKeyImplication; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.TypeFilterConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; + +/** + * A judgement that means that the given tuple of variables will represent a tuple of values that is a member of the extensional relation identified by the given input key. + * @author Bergmann Gabor + * + */ +public class TypeJudgement { + + private IInputKey inputKey; + private Tuple variablesTuple; + /** + * @param inputKey + * @param variablesTuple + */ + public TypeJudgement(IInputKey inputKey, Tuple variablesTuple) { + super(); + this.inputKey = inputKey; + this.variablesTuple = variablesTuple; + } + public IInputKey getInputKey() { + return inputKey; + } + public Tuple getVariablesTuple() { + return variablesTuple; + } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + + ((inputKey == null) ? 0 : inputKey.hashCode()); + result = prime * result + + ((variablesTuple == null) ? 0 : variablesTuple.hashCode()); + return result; + } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof TypeJudgement)) + return false; + TypeJudgement other = (TypeJudgement) obj; + if (inputKey == null) { + if (other.inputKey != null) + return false; + } else if (!inputKey.equals(other.inputKey)) + return false; + if (variablesTuple == null) { + if (other.variablesTuple != null) + return false; + } else if (!variablesTuple.equals(other.variablesTuple)) + return false; + return true; + } + + public Set getDirectlyImpliedJudgements(IQueryMetaContext context) { + Set results = new HashSet(); + results.add(this); + + Collection implications = context.getImplications(this.inputKey); + for (InputKeyImplication inputKeyImplication : implications) { + results.add( + transcribeImplication(inputKeyImplication) + ); + } + + return results; + } + + /** + * @since 1.6 + */ + public Set getWeakenedAlternativeJudgements(IQueryMetaContext context) { + Set results = new HashSet(); + + Collection implications = context.getWeakenedAlternatives(this.inputKey); + for (InputKeyImplication inputKeyImplication : implications) { + results.add( + transcribeImplication(inputKeyImplication) + ); + } + + return results; + } + + /** + * @since 2.0 + */ + public Map> getConditionalImpliedJudgements(IQueryMetaContext context) { + return context.getConditionalImplications(this.inputKey).entrySet().stream().collect(Collectors.toMap( + entry -> transcribeImplication(entry.getKey()), + entry -> entry.getValue().stream().map(this::transcribeImplication).collect(Collectors.toSet()))); + } + + + + private TypeJudgement transcribeImplication(InputKeyImplication inputKeyImplication) { + return new TypeJudgement( + inputKeyImplication.getImpliedKey(), + transcribeVariablesToTuple(inputKeyImplication.getImpliedIndices()) + ); + } + private Tuple transcribeVariablesToTuple(List indices) { + Object[] elements = new Object[indices.size()]; + for (int i = 0; i < indices.size(); ++i) + elements[i] = variablesTuple.get(indices.get(i)); + return Tuples.flatTupleOf(elements); + } + + @Override + public String toString() { + return "TypeJudgement:" + inputKey.getPrettyPrintableName() + "@" + variablesTuple.toString(); + } + + /** + * Creates this judgement as a direct type constraint in the given PBody under construction. + *

pre: the variables tuple must be formed of variables of that PBody. + * @since 1.6 + */ + public PConstraint createConstraintFor(PBody pBody) { + if (inputKey.isEnumerable()) { + return new TypeConstraint(pBody, variablesTuple, inputKey); + } else { + return new TypeFilterConstraint(pBody, variablesTuple, inputKey); + } + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java new file mode 100644 index 00000000..8ea6bb93 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/VariableDeferredPConstraint.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem; + +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; + +/** + * A kind of deferred constraint that can only be checked when a set of deferring variables are all present in a plan. + * + * @author Gabor Bergmann + * + */ +public abstract class VariableDeferredPConstraint extends DeferredPConstraint { + + public VariableDeferredPConstraint(PBody pBody, + Set affectedVariables) { + super(pBody, affectedVariables); + } + + public abstract Set getDeferringVariables(); + + /** + * Refine further if needed + */ + @Override + public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { + return plan.getVisibleVariables().containsAll(getDeferringVariables()); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java new file mode 100644 index 00000000..63a37bbe --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AbstractMemorylessAggregationOperator.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.aggregations; + +/** + * + * An aggregation operator that does not store interim results beyond the final aggregated value. + * @author Gabor Bergmann + * @since 1.4 + */ +public abstract class AbstractMemorylessAggregationOperator + implements IMultisetAggregationOperator +{ + + @Override + public AggregateResult getAggregate(AggregateResult result) { + return result; + } + + @Override + public AggregateResult clone(AggregateResult original) { + return original; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java new file mode 100644 index 00000000..4cc40a2b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/AggregatorType.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.aggregations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import tools.refinery.viatra.runtime.matchers.aggregators.count; + +/** + * The aggregator type annotation describes the type constraints for the selected aggregator. In version 1.4, two kinds of + * aggregators are supported: + * + *

    + *
  1. An aggregator that does not consider any parameter value from the call ({@link count}), just calculates the + * number of matches. This is represented by a single {@link Void} and a single corresponding return type.
  2. + *
  3. An aggregator that considers a single parameter from the call, and executes some aggregate operations over it. + * Such an aggregate operation can be defined over multiple types, where each possible parameter type has a corresponding return type declared.
  4. + *
+ * + * Important! The parameterTypes and returnTypes arrays must have + *
    + *
  • The same number of classes defined each.
  • + *
  • Items are corresponded by index.
  • + *
  • Items should represent data types
  • + *
+ * + * @author Zoltan Ujhelyi + * @since 1.4 + * + */ +@Target({ ElementType.TYPE }) +@Retention(RetentionPolicy.RUNTIME) +@Inherited +public @interface AggregatorType { + + Class[] parameterTypes(); + + Class[] returnTypes(); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java new file mode 100644 index 00000000..e6972544 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/BoundAggregator.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.aggregations; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.common.JavaTransitiveInstancesKey; + +/** + * Augments an aggregator operator with type bindings for the type of values being aggregated and the aggregate result. + *

In case of count, the operator should be null. + * @author Gabor Bergmann + * @since 1.4 + */ +public class BoundAggregator { + private final IMultisetAggregationOperator operator; + private final Class domainType; + private final Class aggregateResultType; + + public BoundAggregator(IMultisetAggregationOperator operator, + Class domainType, + Class aggregateResultType) { + super(); + this.operator = operator; + this.domainType = domainType; + this.aggregateResultType = aggregateResultType; + } + + public IMultisetAggregationOperator getOperator() { + return operator; + } + + public Class getDomainType() { + return domainType; + } + + public Class getAggregateResultType() { + return aggregateResultType; + } + + public IInputKey getDomainTypeAsInputKey() { + return toJavaInputKey(domainType); + } + + public IInputKey getAggregateResultTypeAsInputKey() { + return toJavaInputKey(aggregateResultType); + } + + private static IInputKey toJavaInputKey(Class type) { + if (type==null) { + return null; + } else { + return new JavaTransitiveInstancesKey(type); + } + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java new file mode 100644 index 00000000..c970bd6a --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IAggregatorFactory.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Zoltan Ujhelyi, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.aggregations; + +/** + * + * Describes an aggregation operator keyword, potentially with type polymorphism. The actual runtime + * {@link IMultisetAggregationOperator} that implements the aggregation logic may depend on the type context. + * + *

+ * Implementors are suggested to use lower-case classnames (as it will end up in the language) and are required use the + * annotation {@link AggregatorType} to indicate type inference rules. + * + *

+ * Important! Implemented aggregators must be (1) deterministic (2) pure and (3)support incremental + * value updates in the internal operation. + * + * @author Zoltan Ujhelyi + * @since 1.4 + */ + +public interface IAggregatorFactory { + + /** + * Given type parameters selected from {@link AggregatorType} annotations, returns a run-time aggregator operator + * that is bound to the actual types. + * + * @param domainClass + * Java type of the values that are being aggregated + * @return the actual run-time aggregator logic, with type bindings + */ + public BoundAggregator getAggregatorLogic(Class domainClass); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java new file mode 100644 index 00000000..3bc22274 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/aggregations/IMultisetAggregationOperator.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.aggregations; + +import java.util.Collection; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.aggregators.ExtremumOperator; + +/** + * A single column aggregator is used to incrementally compute the aggregate of a multiset of values according to an aggregator operator. + * + *

The operator provides two methods of computation:

    + *
  • Stateless aggregation of an explicit multiset, provided by {@link #aggregateStatelessly(Collection)}.
  • + *
  • Incremental aggregation, provided by {@link #createNeutral()}, {@link #update(Object, Object, boolean)}, {@link #isNeutral(Object)}, {@link #getAggregate(Object)}. + *
+ * + *

In case of incremental computation, the aggregable multiset is conceptual; it is not represented by an explicit Collection object, but its update operations are tracked. + * + *

In case of incremental computation, internal results, potentially distinct from the final aggregate result, may be stored in a helper data structure called accumulator. + * The goal of this distinction is that the final result may not be sufficient for incremental updates (see e.g. {@link ExtremumOperator}). + * + * @author Gabor Bergmann + * + * @param the type of elements to be aggregated. + * @param the type used to store the interim results of the aggregate computation, + * that may be incrementally refreshed upon updates to the multiset, and that can easily yield the final result. + * @param the type of the final result of the aggregation to be output. + * + * @since 1.4 + */ +public interface IMultisetAggregationOperator { + + /** + * A textual description of the operator. + */ + String getShortDescription(); + + /** + * A name or identifier of the operator. + */ + String getName(); + + /** + * @return the neutral element, i.e. the interim result of aggregating an empty multiset. + */ + Accumulator createNeutral(); + + /** + * @return true if the interim result is equivalent to the neutral element, as if there are no values in the multiset. + * Must return true if the multiset is empty. + */ + boolean isNeutral(Accumulator result); + + /** + * @return an updated intermediate result, + * changed to reflect that a given object was added to / removed from the multiset + * (as indicated by the parameter isInsertion) + */ + Accumulator update(Accumulator oldResult, Domain updateValue, boolean isInsertion); + + /** + * @return the aggregate result obtained from the given intermediate result. + * May be null to indicate that the current multiset cannot be aggregated (e.g. 0 elements have no minimum). + */ + AggregateResult getAggregate(Accumulator result); + + /** + * Calculates the aggregate results from a given stream of values; all values are considered as inserted + * @return the aggregate result, or null if no result can be calculated (e.g. because of an empty stream) + * @since 2.0 + */ + AggregateResult aggregateStream(Stream stream); + + /** + * Clones the given accumulator (with all its internal contents). + */ + default Accumulator clone(Accumulator original) { + throw new UnsupportedOperationException(); + } + + /** + * Combines the given aggregate result and accumulator into a single aggregate result. + */ + default AggregateResult combine(AggregateResult left, Accumulator right) { + throw new UnsupportedOperationException(); + } + + default boolean contains(Domain value, Accumulator accumulator) { + throw new UnsupportedOperationException(); + } + + /** + * Pretty prints the contents of the given accumulator. + */ + default String prettyPrint(final Accumulator accumulator) { + return accumulator.toString(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java new file mode 100644 index 00000000..e3f28cff --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/analysis/QueryAnalyzer.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.analysis; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.helpers.FunctionalDependencyHelper; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; +import tools.refinery.viatra.runtime.matchers.psystem.annotations.ParameterReference; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * Object responsible for computing and caching static query analysis results. + *

Any client can instantiate this to statically analyze queries. + * Query backends should share an instance obtained via {@link IQueryBackendContext} to save resources. + *

Precondition: all involved queries must be initialized. + * @noinstantiate Considered unstable API; subject to change in future versions. + * Either use the analyzer provided by {@link IQueryBackendContext}, or anticipate + * potential future breakage when instantiating your own analyzer. + * @author Gabor Bergmann + * @since 1.5 + */ +public final class QueryAnalyzer { + + private IQueryMetaContext metaContext; + + public QueryAnalyzer(IQueryMetaContext metaContext) { + this.metaContext = metaContext; + } + + // Functional dependencies + + /** + * Maps query and strictness to functional dependencies + */ + private Map, Set>> strictFunctionalDependencyGuarantees = + new HashMap<>(); + private Map, Set>> softFunctionalDependencyGuarantees = + new HashMap<>(); + + /** + * Functional dependency information, expressed on query parameters, that the match set of the query is guaranteed to respect. + *

The type dependencies shall be expressed on the parameter index integers, NOT the {@link PParameter} object. + * @return a non-null map of functional dependencies on parameters that can be processed by {@link FunctionalDependencyHelper} + * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; + * if false, user-provided soft dependencies (@FunctionalDependency) are included as well, that are anticipated but not guaranteed by the storage mechanism; + * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance + * @since 1.5 + */ + public Map, Set> getProjectedFunctionalDependencies(PQuery query, boolean strict) { + Map, Set>> guaranteeStore = strict ? strictFunctionalDependencyGuarantees : softFunctionalDependencyGuarantees; + Map, Set> dependencies = guaranteeStore.get(query); + // Why not computeIfAbsent? See Bug 532507 + // Invoked method #computeFunctionalDependencies may trigger functional dependency computation for called queries; + // and may thus recurs back into #getProjectedFunctionalDependencies, causing a ConcurrentModificationException + // if the called query has not been previously analyzed. + // + // Note: if patterns are recursive, the empty accumulator will be found in the store + // (this yields a safe lower estimate and guarantees termination for #getProjectedFunctionalDependencies) + // But this case probably will not occur due to recursive queries having a disjunction at some point, + // and thus ignored by #computeFunctionalDependencies + if (dependencies == null) { + dependencies = new HashMap<>(); // accumulator + guaranteeStore.put(query, dependencies); + computeFunctionalDependencies(dependencies, query, strict); + } + return dependencies; + } + + private void computeFunctionalDependencies(Map, Set> accumulator, PQuery query, boolean strict) { + Set bodies = query.getDisjunctBodies().getBodies(); + if (bodies.size() == 1) { // no support for recursion or disjunction + + PBody body = bodies.iterator().next(); + + // collect parameter variables + Map parameters = body.getSymbolicParameters().stream() + .collect(Collectors.toMap(ExportedParameter::getParameterVariable, + param -> query.getParameters().indexOf(param.getPatternParameter()))); + + // collect all internal dependencies + Map, Set> internalDependencies = + getFunctionalDependencies(body.getConstraints(), strict); + + // project onto parameter variables + Map, Set> projectedDeps = + FunctionalDependencyHelper.projectDependencies(internalDependencies, parameters.keySet()); + + // translate into indices + for (Entry, Set> entry : projectedDeps.entrySet()) { + Set left = new HashSet(); + Set right = new HashSet(); + for (PVariable pVariable : entry.getKey()) { + left.add(parameters.get(pVariable)); + } + for (PVariable pVariable : entry.getValue()) { + right.add(parameters.get(pVariable)); + } + accumulator.put(left, right); + } + + } else { + // Disjunctive case, no dependencies are inferred + // TODO: we can still salvage the intersection of dependencies IF + // - all bodies have disjoint match sets + // - and we avoid recursion + } + + // add annotation-based soft dependencies (regardless of number of bodies) + if (!strict) { + outer: + for (PAnnotation annotation : query.getAnnotationsByName("FunctionalDependency")) { + Set lefts = new HashSet(); + Set rights = new HashSet(); + + for (Object object : annotation.getAllValues("forEach")) { + ParameterReference parameter = (ParameterReference) object; + Integer position = query.getPositionOfParameter(parameter.getName()); + if (position == null) continue outer; + lefts.add(position); + } + for (Object object : annotation.getAllValues("unique")) { + ParameterReference parameter = (ParameterReference) object; + Integer position = query.getPositionOfParameter(parameter.getName()); + if (position == null) continue outer; + rights.add(position); + } + + FunctionalDependencyHelper.includeDependency(accumulator, lefts, rights); + } + } + } + + /** + * Functional dependency information, expressed on PVariables within a body, that the selected constraints imply. + * @return a non-null map of functional dependencies on PVariables that can be processed by {@link FunctionalDependencyHelper} + * @param constraints the set of constraints whose consequences will be analyzed + * @param strict if true, only "hard" dependencies are taken into account that are strictly enforced by the model representation; + * if false, user-provided soft dependencies (@FunctionalDependency) are included as well, that are anticipated but not guaranteed by the storage mechanism; + * use true if superfluous dependencies may taint the correctness of a computation, false if they would merely impact performance + * @since 1.5 + */ + public Map, Set> getFunctionalDependencies(Set constraints, boolean strict) { + Map, Set> accumulator = new HashMap, Set>(); + for (PConstraint pConstraint : constraints){ + if (pConstraint instanceof PositivePatternCall) { + // use query analysis results instead + PositivePatternCall call = (PositivePatternCall) pConstraint; + PQuery query = call.getSupplierKey(); + Map, Set> paramDependencies = getProjectedFunctionalDependencies(query, strict); + for (Entry, Set> entry : paramDependencies.entrySet()) { + Set lefts = new HashSet(); + Set rights = new HashSet(); + + for (Integer index : entry.getKey()) { + lefts.add(call.getVariableInTuple(index)); + } + for (Integer index : entry.getValue()) { + rights.add(call.getVariableInTuple(index)); + } + + FunctionalDependencyHelper.includeDependency(accumulator, + lefts, rights); + } + } else { + // delegate to PConstraint + FunctionalDependencyHelper.includeDependencies(accumulator, + pConstraint.getFunctionalDependencies(metaContext)); + } + } + return accumulator; + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java new file mode 100644 index 00000000..c4fbe0e9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/PAnnotation.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.annotations; + +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.BiConsumer; + +import org.eclipse.collections.api.multimap.MutableMultimap; +import org.eclipse.collections.impl.multimap.list.FastListMultimap; + +/** + * A container describing query annotations + * @author Zoltan Ujhelyi + * + */ +public class PAnnotation { + + private final String name; + private MutableMultimap attributes = FastListMultimap.newMultimap(); + + public PAnnotation(String name) { + this.name = name; + + } + + /** + * Adds an attribute to the annotation + * @param attributeName + * @param value + */ + public void addAttribute(String attributeName, Object value) { + attributes.put(attributeName, value); + } + + /** + * Return the name of the annotation + */ + public String getName() { + return name; + } + + /** + * Returns the value of the first occurrence of an attribute + * @param attributeName + * @return the attribute value, or null, if attribute is not available + * @since 2.0 + */ + public Optional getFirstValue(String attributeName) { + return getAllValues(attributeName).stream().findFirst(); + } + + /** + * Returns the value of the first occurrence of an attribute + * @param attributeName + * @return the attribute value, or null, if attribute is not available + * @since 2.0 + */ + public Optional getFirstValue(String attributeName, Class clazz) { + return getAllValues(attributeName).stream().filter(clazz::isInstance).map(clazz::cast).findFirst(); + } + + /** + * Returns all values of a selected attribute + * @param attributeName + * @return a non-null, but possibly empty list of attributes + */ + public List getAllValues(String attributeName) { + return attributes.get(attributeName).toList(); + } + + /** + * Executes a consumer over all attributes. A selected attribute name (key) can appear (and thus consumed) multiple times. + * @since 2.0 + */ + public void forEachValue(BiConsumer consumer) { + attributes.forEachKeyValue(consumer::accept); + } + + /** + * Returns a set of all attribute names used in this annotation + * @since 2.1 + */ + public Set getAllAttributeNames() { + return attributes.keySet().toSet(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java new file mode 100644 index 00000000..c67e9046 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/annotations/ParameterReference.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.annotations; + +/** + * An annotation parameter referencing a query parameter by name. Does not check whether the parameter exists. + * + * @author Zoltan Ujhelyi + * + */ +public class ParameterReference { + + final String name; + + public ParameterReference(String name) { + super(); + this.name = name; + } + + public String getName() { + return name; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java new file mode 100644 index 00000000..56f86e89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/AggregatorConstraint.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; +import tools.refinery.viatra.runtime.matchers.psystem.aggregations.BoundAggregator; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; + +/** + * The PSystem representation of an aggregation. + * + * @author Tamas Szabo + * @since 1.4 + */ +public class AggregatorConstraint extends PatternCallBasedDeferred implements ITypeInfoProviderConstraint { + + protected PVariable resultVariable; + private BoundAggregator aggregator; + protected int aggregatedColumn; + + public AggregatorConstraint(BoundAggregator aggregator, PBody pBody, Tuple actualParametersTuple, PQuery query, + PVariable resultVariable, int aggregatedColumn) { + super(pBody, actualParametersTuple, query, Collections.singleton(resultVariable)); + this.resultVariable = resultVariable; + this.aggregatedColumn = aggregatedColumn; + this.aggregator = aggregator; + } + + public int getAggregatedColumn() { + return this.aggregatedColumn; + } + + public BoundAggregator getAggregator() { + return this.aggregator; + } + + @Override + public Set getDeducedVariables() { + return Collections.singleton(resultVariable); + } + + @Override + public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { + final Map, Set> result = new HashMap, Set>(); + result.put(getDeferringVariables(), getDeducedVariables()); + return result; + } + + @Override + protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { + if (resultVariable.equals(obsolete)) + resultVariable = replacement; + } + + @Override + protected Set getCandidateQuantifiedVariables() { + return actualParametersTuple. getDistinctElements(); + } + + @Override + protected String toStringRest() { + return query.getFullyQualifiedName() + "@" + actualParametersTuple.toString() + "->" + + resultVariable.toString(); + } + + public PVariable getResultVariable() { + return resultVariable; + } + + @Override + public Set getImpliedJudgements(IQueryMetaContext context) { + Set result = new HashSet(); + IInputKey aggregateResultType = aggregator.getAggregateResultTypeAsInputKey(); + if (aggregateResultType != null) { + result.add(new TypeJudgement(aggregateResultType, Tuples.staticArityFlatTupleOf(resultVariable))); + } + return result; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java new file mode 100644 index 00000000..7bc949a8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/BaseTypeSafeConstraint.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.Collections; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; +import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; + +/** + * @author Gabor Bergmann + * + */ +public abstract class BaseTypeSafeConstraint extends + VariableDeferredPConstraint { + + protected Set inputVariables; + protected PVariable outputVariable; + + public PVariable getOutputVariable() { + return outputVariable; + } + + /** + * @param pBody + * @param inputVariables + * @param outputVariable null iff no output (check-only) + */ + public BaseTypeSafeConstraint(PBody pBody, + Set inputVariables, final PVariable outputVariable) { + super(pBody, + (outputVariable == null) ? + inputVariables : + Stream.concat(inputVariables.stream(), Stream.of(outputVariable)).collect(Collectors.toSet()) + ); + this.inputVariables = inputVariables; + this.outputVariable = outputVariable; + } + + @Override + public Set getDeducedVariables() { + if (outputVariable == null) + return Collections.emptySet(); + else + return Collections.singleton(outputVariable); + } + + @Override + public Set getDeferringVariables() { + return inputVariables; + } + + @Override + public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { + if (super.isReadyAt(plan, context)) { + return checkTypeSafety(plan, context) == null; + } + return false; + } + + /** + * Checks whether all type restrictions are already enforced on affected variables. + * + * @param plan + * @return a variable whose type safety is not enforced yet, or null if the plan is typesafe + */ + public PVariable checkTypeSafety(SubPlan plan, IQueryMetaContext context) { + Set impliedJudgements = plan.getAllImpliedTypeJudgements(context); + + for (PVariable pVariable : inputVariables) { + Set allTypeRestrictionsForVariable = pBody.getAllUnaryTypeRestrictions(context).get(pVariable); + if (allTypeRestrictionsForVariable != null && !impliedJudgements.containsAll(allTypeRestrictionsForVariable)) + return pVariable; + } + return null; + } + + @Override + protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { + if (inputVariables.remove(obsolete)) + inputVariables.add(replacement); + if (outputVariable == obsolete) + outputVariable = replacement; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java new file mode 100644 index 00000000..b978b62c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Equality.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.SubPlan; +import tools.refinery.viatra.runtime.matchers.psystem.DeferredPConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; + +/** + * @author Gabor Bergmann + * + */ +public class Equality extends DeferredPConstraint { + + private PVariable who; + private PVariable withWhom; + + public Equality(PBody pBody, PVariable who, PVariable withWhom) { + super(pBody, buildSet(who, withWhom)); + this.who = who; + this.withWhom = withWhom; + } + + private static Set buildSet(PVariable who, PVariable withWhom) { + Set set = new HashSet(); + set.add(who); + set.add(withWhom); + return set; + } + + /** + * An equality is moot if it compares the a variable with itself. + * + * @return true, if the equality is moot + */ + public boolean isMoot() { + return who.equals(withWhom); + } + + @Override + public void doReplaceVariable(PVariable obsolete, PVariable replacement) { + if (obsolete.equals(who)) + who = replacement; + if (obsolete.equals(withWhom)) + withWhom = replacement; + } + + @Override + protected String toStringRest() { + return who.getName() + "=" + withWhom.getName(); + } + + public PVariable getWho() { + return who; + } + + public PVariable getWithWhom() { + return withWhom; + } + + @Override + public Set getDeducedVariables() { + return Collections.emptySet(); + } + + @Override + public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { + final Map, Set> result = new HashMap, Set>(); + result.put(Collections.singleton(who), Collections.singleton(withWhom)); + result.put(Collections.singleton(withWhom), Collections.singleton(who)); + return result; + } + + @Override + public boolean isReadyAt(SubPlan plan, IQueryMetaContext context) { + return plan.getVisibleVariables().contains(who) && plan.getVisibleVariables().contains(withWhom); + // will be replaced by || if copierNode is available; + // until then, LayoutHelper.unifyVariablesAlongEqualities(PSystem) is + // recommended. + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java new file mode 100644 index 00000000..80706792 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExportedParameter.java @@ -0,0 +1,108 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.Collections; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * @author Gabor Bergmann + * + */ +public class ExportedParameter extends VariableDeferredPConstraint { + PVariable parameterVariable; + final String parameterName; + final PParameter patternParameter; + + /** + * @since 1.4 + */ + public ExportedParameter(PBody pBody, PVariable parameterVariable, PParameter patternParameter) { + super(pBody, Collections.singleton(parameterVariable)); + this.parameterVariable = parameterVariable; + this.patternParameter = patternParameter; + parameterName = patternParameter.getName(); + } + + @Override + public void doReplaceVariable(PVariable obsolete, PVariable replacement) { + if (obsolete.equals(parameterVariable)) + parameterVariable = replacement; + } + + @Override + protected String toStringRest() { + Object varName = parameterVariable.getName(); + return parameterName.equals(varName) ? parameterName : parameterName + "(" + varName + ")"; + } + + @Override + public Set getDeducedVariables() { + return Collections.emptySet(); + } + + /** + * The name of the parameter; usually, it is expected that {@link #getParameterVariable()} is more useful, except + * maybe for debugging purposes. + * + * @return a non-null name of the parameter + */ + public String getParameterName() { + return parameterName; + } + + public PVariable getParameterVariable() { + return parameterVariable; + } + + /** + * @since 1.4 + */ + public PParameter getPatternParameter() { + if (patternParameter == null) { + PQuery query = pBody.getPattern(); + Integer index = query.getPositionOfParameter(parameterName); + if (index == null) { + throw new IllegalStateException(String.format("Pattern %s does not have a parameter named %s", + query.getFullyQualifiedName(), parameterName)); + } + return query.getParameters().get(index); + } else { + return patternParameter; + } + } + + @Override + public Set getDeferringVariables() { + return Collections.singleton(parameterVariable); + } + + @Override + public void checkSanity() { + super.checkSanity(); + if (!parameterVariable.isDeducable()) { + String[] args = { parameterName }; + String msg = "Impossible to match pattern: " + + "exported pattern variable {1} can not be determined based on the pattern constraints. " + + "HINT: certain constructs (e.g. negative patterns or check expressions) cannot output symbolic parameters."; + String shortMsg = "Could not deduce value of parameter"; + throw new QueryProcessingException(msg, args, shortMsg, null); + } + + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java new file mode 100644 index 00000000..06688c36 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/ExpressionEvaluation.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; + +/** + * @author Zoltan Ujhelyi + * + */ +public class ExpressionEvaluation extends BaseTypeSafeConstraint { + + private IExpressionEvaluator evaluator; + private boolean isUnwinding; + + public ExpressionEvaluation(PBody pBody, IExpressionEvaluator evaluator, PVariable outputVariable) { + this(pBody, evaluator, outputVariable, false); + } + + /** + * @since 2.4 + */ + public ExpressionEvaluation(PBody pBody, IExpressionEvaluator evaluator, PVariable outputVariable, + boolean isUnwinding) { + super(pBody, getPVariablesOfExpression(pBody, evaluator), outputVariable); + this.evaluator = evaluator; + this.isUnwinding = isUnwinding; + } + + /** + * @since 2.4 + */ + public boolean isUnwinding() { + return isUnwinding; + } + + public IExpressionEvaluator getEvaluator() { + return evaluator; + } + + @Override + protected String toStringRest() { + return Tuples.flatTupleOf(new ArrayList(inputVariables).toArray()).toString() + "|=" + + evaluator.getShortDescription(); + } + + @Override + public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { + if (outputVariable == null) + return Collections.emptyMap(); + else + return Collections.singletonMap(inputVariables, Collections.singleton(outputVariable)); + } + + private static Set getPVariablesOfExpression(PBody pBody, IExpressionEvaluator evaluator) { + // use a linked set, so that the variables will come in the order of the parameters + Set result = new LinkedHashSet(); + for (String name : evaluator.getInputParameterNames()) { + PVariable variable = pBody.getOrCreateVariableByName(name); + result.add(variable); + } + return result; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java new file mode 100644 index 00000000..5cac33dc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/Inequality.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; + +/** + * @author Gabor Bergmann + * + */ +public class Inequality extends VariableDeferredPConstraint { + + private PVariable who; + private PVariable withWhom; + + /** + * The inequality constraint is weak if it can be ignored when who is the same as withWhom, or if any if them is + * undeducible. + */ + private boolean weak; + + public Inequality(PBody pBody, PVariable who, PVariable withWhom) { + this(pBody, who, withWhom, false); + } + + public Inequality(PBody pBody, PVariable who, PVariable withWhom, + boolean weak) { + super(pBody, new HashSet<>(Arrays.asList(who, withWhom) )); + this.who = who; + this.withWhom = withWhom; + this.weak = weak; + } + + // private Inequality( + // PSystem pSystem, + // PVariable subject, Set inequals) + // { + // super(pSystem, include(inequals, subject)); + // this.subject = subject; + // this.inequals = inequals; + // } + + // private static HashSet include(Set inequals, PVariable subject) { + // HashSet hashSet = new HashSet(inequals); + // hashSet.add(subject); + // return hashSet; + // } + + @Override + public Set getDeferringVariables() { + return getAffectedVariables(); + } + + // private static int[] mapIndices(Map variablesIndex, Set keys) { + // int[] result = new int[keys.size()]; + // int k = 0; + // for (PVariable key : keys) { + // result[k++] = variablesIndex.get(key); + // } + // return result; + // } + + // @Override + // public IFoldablePConstraint getIncorporator() { + // return incorporator; + // } + // + // @Override + // public void registerIncorporatationInto(IFoldablePConstraint incorporator) { + // this.incorporator = incorporator; + // } + // + // @Override + // public boolean incorporate(IFoldablePConstraint other) { + // if (other instanceof Inequality) { + // Inequality other2 = (Inequality) other; + // if (subject.equals(other2.subject)) { + // Set newInequals = new HashSet(inequals); + // newInequals.addAll(other2.inequals); + // return new Inequality(buildable, subject, newInequals); + // } + // } else return false; + // } + + @Override + protected String toStringRest() { + return who.toString() + (isWeak() ? "!=?" : "!=") + withWhom.toString(); + } + + @Override + public void doReplaceVariable(PVariable obsolete, PVariable replacement) { + if (obsolete.equals(who)) + who = replacement; + if (obsolete.equals(withWhom)) + withWhom = replacement; + } + + @Override + public Set getDeducedVariables() { + return Collections.emptySet(); + } + + /** + * The inequality constraint is weak if it can be ignored when who is the same as withWhom, or if any if them is + * undeducible. + * + * @return the weak + */ + public boolean isWeak() { + return weak; + } + + /** + * A weak inequality constraint is eliminable if who is the same as withWhom, or if any if them is undeducible. + */ + public boolean isEliminable() { + return isWeak() && (who.equals(withWhom) || !who.isDeducable() || !withWhom.isDeducable()); + } + + /** + * Eliminates a weak inequality constraint if it can be ignored when who is the same as withWhom, or if any if them + * is undeducible. + */ + public void eliminateWeak() { + if (isEliminable()) + delete(); + } + + public PVariable getWho() { + return who; + } + + public PVariable getWithWhom() { + return withWhom; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java new file mode 100644 index 00000000..87d9d9fc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/NegativePatternCall.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.Collections; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * @author Gabor Bergmann + * + */ +public class NegativePatternCall extends PatternCallBasedDeferred { + + public NegativePatternCall(PBody pBody, Tuple actualParametersTuple, PQuery query) { + super(pBody, actualParametersTuple, query); + } + + @Override + public Set getDeducedVariables() { + return Collections.emptySet(); + } + + /** + * @return all variables that may potentially be quantified they are not used anywhere else + */ + @Override + protected Set getCandidateQuantifiedVariables() { + return getAffectedVariables(); + } + + @Override + protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { + } + + @Override + protected String toStringRest() { + return "!" + query.getFullyQualifiedName() + "@" + actualParametersTuple.toString(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java new file mode 100644 index 00000000..93eeffec --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternCallBasedDeferred.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; +import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * @author Gabor Bergmann + * + */ +public abstract class PatternCallBasedDeferred extends VariableDeferredPConstraint implements IQueryReference { + + protected Tuple actualParametersTuple; + + protected abstract void doDoReplaceVariables(PVariable obsolete, PVariable replacement); + + protected abstract Set getCandidateQuantifiedVariables(); + + protected PQuery query; + private Set deferringVariables; + + public PatternCallBasedDeferred(PBody pBody, Tuple actualParametersTuple, + PQuery pattern, Set additionalAffectedVariables) { + super(pBody, union(actualParametersTuple. getDistinctElements(), additionalAffectedVariables)); + this.actualParametersTuple = actualParametersTuple; + this.query = pattern; + } + + public PatternCallBasedDeferred(PBody pBody, Tuple actualParametersTuple, + PQuery pattern) { + this(pBody, actualParametersTuple, pattern, Collections. emptySet()); + } + + private static Set union(Set a, Set b) { + Set result = new HashSet(); + result.addAll(a); + result.addAll(b); + return result; + } + + @Override + public Set getDeferringVariables() { + if (deferringVariables == null) { + deferringVariables = new HashSet(); + for (PVariable var : getCandidateQuantifiedVariables()) { + if (var.isDeducable()) + deferringVariables.add(var); + } + } + return deferringVariables; + } + + @Override + public void checkSanity() { + super.checkSanity(); + for (Object obj : this.actualParametersTuple.getDistinctElements()) { + PVariable var = (PVariable) obj; + if (!getDeferringVariables().contains(var)) { + // so this is a free variable of the NAC / aggregation? + for (PConstraint pConstraint : var.getReferringConstraints()) { + if (pConstraint != this + && !(pConstraint instanceof Equality && ((Equality) pConstraint).isMoot())) + throw new QueryProcessingException ( + "Variable {1} of constraint {2} is not a positively determined part of the pattern, yet it is also affected by {3}.", + new String[] { var.toString(), this.toString(), pConstraint.toString() }, + "Read-only variable can not be deduced", null); + } + } + } + + } + +// public SubPlan getSidePlan(IOperationCompiler compiler) throws QueryPlannerException { +// SubPlan sidePlan = compiler.patternCallPlan(actualParametersTuple, query); +// sidePlan = BuildHelper.enforceVariableCoincidences(compiler, sidePlan); +// return sidePlan; +// } + + @Override + protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { + if (deferringVariables != null) { + // FAIL instead of hopeless attempt to fix + // if (deferringVariables.remove(obsolete)) deferringVariables.add(replacement); + throw new IllegalStateException("Cannot replace variables on " + this + + " when deferring variables have already been identified."); + } + actualParametersTuple = actualParametersTuple.replaceAll(obsolete, replacement); + doDoReplaceVariables(obsolete, replacement); + } + + public Tuple getActualParametersTuple() { + return actualParametersTuple; + } + + @Override + public PQuery getReferredQuery() { + return query; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java new file mode 100644 index 00000000..0c40d91e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/PatternMatchCounter.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * @author Gabor Bergmann + */ +public class PatternMatchCounter extends PatternCallBasedDeferred { + + private PVariable resultVariable; + + public PatternMatchCounter(PBody pBody, Tuple actualParametersTuple, + PQuery query, PVariable resultVariable) { + super(pBody, actualParametersTuple, query, Collections.singleton(resultVariable)); + this.resultVariable = resultVariable; + } + + @Override + public Set getDeducedVariables() { + return Collections.singleton(resultVariable); + } + + @Override + public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { + final Map, Set> result = new HashMap, Set>(); + result.put(getDeferringVariables(), getDeducedVariables()); + return result; + } + + @Override + protected void doDoReplaceVariables(PVariable obsolete, PVariable replacement) { + if (resultVariable.equals(obsolete)) + resultVariable = replacement; + } + + @Override + protected Set getCandidateQuantifiedVariables() { + return actualParametersTuple. getDistinctElements(); + } + + + @Override + protected String toStringRest() { + return query.getFullyQualifiedName() + "@" + actualParametersTuple.toString() + "->" + + resultVariable.toString(); + } + + public PVariable getResultVariable() { + return resultVariable; + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java new file mode 100644 index 00000000..336a83fb --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/RelationEvaluation.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2010-2022, Tamas Szabo, GitHub + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.List; + +import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference; +import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * A constraint which prescribes the evaluation of custom Java logic that takes an arbitrary number of input relations + * and produces one output relation. Contrast this to {@link ExpressionEvaluation}, which produces a single output value + * given an input tuple. + * + * The assumption is that the relation evaluation logic is not incremental, that is, it can only perform from-scratch + * computation of the output relation given the complete input relations. To this end, the relation evaluator always + * receives the complete input relations with all their contents as input. However, the evaluator engine makes sure that + * the output of the relation evaluation is at least "seemingly" incremental. This means that the underlying computation + * network computes the delta on the output compared to the previous output and only propagates the delta further. + * + * @author Tamas Szabo + * + * @since 2.8 + * + */ +public class RelationEvaluation extends EnumerablePConstraint implements IMultiQueryReference { + + private final IRelationEvaluator evaluator; + private final List inputQueries; + + public RelationEvaluation(final PBody body, final Tuple variablesTuple, final List inputQueries, + final IRelationEvaluator evaluator) { + super(body, variablesTuple); + this.evaluator = evaluator; + this.inputQueries = inputQueries; + } + + public IRelationEvaluator getEvaluator() { + return this.evaluator; + } + + @Override + public List getReferredQueries() { + return this.inputQueries; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java new file mode 100644 index 00000000..8b6e29ef --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicdeferred/TypeFilterConstraint.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.basicdeferred; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; +import tools.refinery.viatra.runtime.matchers.psystem.VariableDeferredPConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * Represents a non-enumerable type constraint that asserts that values substituted for the given tuple of variables + * form a tuple that belongs to a (typically non-enumerable) extensional relation identified by an {@link IInputKey}. + * + *

The InputKey is typically not enumerable. If it is enumerable, use {@link TypeConstraint} instead, so that the PConstraint carries over the property of enumerability. + * + * @author Bergmann Gabor + * + */ +public class TypeFilterConstraint extends VariableDeferredPConstraint implements + ITypeConstraint { + + private Tuple variablesTuple; + private IInputKey inputKey; + + private TypeJudgement equivalentJudgement; + + + public TypeFilterConstraint(PBody pBody, Tuple variablesTuple, IInputKey inputKey) { + super(pBody, variablesTuple. getDistinctElements()); + this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); + + this.variablesTuple = variablesTuple; + this.inputKey = inputKey; + + if (variablesTuple.getSize() != inputKey.getArity()) + throw new IllegalArgumentException( + this.getClass().getSimpleName() + + " applied for variable tuple " + variablesTuple + " having wrong arity for input key " + + inputKey); + } + + + + public Tuple getVariablesTuple() { + return variablesTuple; + } + + public IInputKey getInputKey() { + return inputKey; + } + + @Override + public TypeJudgement getEquivalentJudgement() { + return equivalentJudgement; + } + + @Override + protected void doReplaceVariable(PVariable obsolete, PVariable replacement) { + variablesTuple = variablesTuple.replaceAll(obsolete, replacement); + this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); + } + + @Override + public Set getImpliedJudgements(IQueryMetaContext context) { + return Collections.singleton(equivalentJudgement); + } + + @Override + public Set getDeducedVariables() { + return Collections.emptySet(); + } + + @Override + public Set getDeferringVariables() { + return getAffectedVariables(); + } + + @Override + protected String toStringRest() { + return inputKey.getPrettyPrintableName() + "@" + variablesTuple; + } + + @Override + public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { + return TypeConstraintUtil.getFunctionalDependencies(context, inputKey, variablesTuple); + } + + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java new file mode 100644 index 00000000..7bbf7118 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/AbstractTransitiveClosure.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; + +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; +import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * @since 2.0 + */ +public abstract class AbstractTransitiveClosure extends KeyedEnumerablePConstraint implements IQueryReference, ITypeInfoProviderConstraint { + + public AbstractTransitiveClosure(PBody pBody, Tuple variablesTuple, PQuery supplierKey) { + super(pBody, variablesTuple, supplierKey); + } + + @Override + public PQuery getReferredQuery() { + return supplierKey; + } + + /** + * @since 1.3 + */ + @Override + public Set getImpliedJudgements(IQueryMetaContext context) { + return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java new file mode 100644 index 00000000..e3dae240 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryReflexiveTransitiveClosure.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Zoltan Ujhelyi, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * For a binary base pattern over an enumerable universe type, computes the reflexive transitive closure (base)* + * + * @author Gabor Bergmann, Zoltan Ujhelyi + * @since 2.0 + */ +public class BinaryReflexiveTransitiveClosure extends AbstractTransitiveClosure { + + private final IInputKey universeType; + + public BinaryReflexiveTransitiveClosure(PBody pBody, Tuple variablesTuple, + PQuery pattern, IInputKey universeType) { + super(pBody, variablesTuple, pattern); + this.universeType = universeType; + } + + @Override + protected String keyToString() { + return supplierKey.getFullyQualifiedName() + "*"; + } + + /** + * Returns the type whose instances should be returned as 0-long paths. + * @since 2.0 + */ + public IInputKey getUniverseType() { + return universeType; + } + + @Override + public void checkSanity() { + if (!universeType.isEnumerable() || universeType.getArity() != 1) { + throw new QueryProcessingException( + String.format("Invalid universe type %s - it should be enumerable and must have an arity of 1.", + universeType.getPrettyPrintableName()), + pBody.getPattern()); + } + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java new file mode 100644 index 00000000..716d043b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/BinaryTransitiveClosure.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; + +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * For a binary base pattern, computes the irreflexive transitive closure (base)+ + * + * @author Gabor Bergmann + * + */ +public class BinaryTransitiveClosure extends AbstractTransitiveClosure { + + public BinaryTransitiveClosure(PBody pBody, Tuple variablesTuple, + PQuery pattern) { + super(pBody, variablesTuple, pattern); + } + + @Override + protected String keyToString() { + return supplierKey.getFullyQualifiedName() + "+"; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java new file mode 100644 index 00000000..10da2e21 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/Connectivity.java @@ -0,0 +1,11 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; + +public enum Connectivity { + WEAK, + STRONG; +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java new file mode 100644 index 00000000..251146c8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/ConstantValue.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; + +/** + * @author Gabor Bergmann + * + */ +public class ConstantValue extends KeyedEnumerablePConstraint { + + private PVariable variable; + + public ConstantValue(PBody pBody, PVariable variable, Object value) { + super(pBody, Tuples.staticArityFlatTupleOf(variable), value); + this.variable = variable; + } + + @Override + protected String keyToString() { + return supplierKey.toString(); + } + + /** + * @since 1.7 + */ + public PVariable getVariable() { + return variable; + } + + @Override + public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { + final Map, Set> result = new HashMap, Set>(); + final Set emptySet = Collections.emptySet(); // a constant value is functionally determined by everything + result.put(emptySet, Collections.singleton(getVariableInTuple(0))); + return result; + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java new file mode 100644 index 00000000..25ab34b4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/PositivePatternCall.java @@ -0,0 +1,76 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; + +import java.util.HashSet; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.IQueryReference; +import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; + +/** + * @author Gabor Bergmann + * + */ +public class PositivePatternCall extends KeyedEnumerablePConstraint implements IQueryReference, ITypeInfoProviderConstraint { + + public PositivePatternCall(PBody pBody, Tuple variablesTuple, + PQuery pattern) { + super(pBody, variablesTuple, pattern); + } + + @Override + protected String keyToString() { + return supplierKey.getFullyQualifiedName(); + } + + // Note: #getFunctionalDependencies is intentionally not implemented - use QueryAnalyzer instead! +// @Override +// public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { +// return super.getFunctionalDependencies(context); +// } + + @Override + public PQuery getReferredQuery() { + return supplierKey; + } + + @Override + public Set getImpliedJudgements(IQueryMetaContext context) { + return getTypesImpliedByCall(supplierKey, variablesTuple); + } + + /** + * @since 1.3 + */ + public static Set getTypesImpliedByCall(PQuery calledQuery, Tuple actualParametersTuple) { + Set result = new HashSet(); + for (TypeJudgement parameterJudgement : calledQuery.getTypeGuarantees()) { + IInputKey inputKey = parameterJudgement.getInputKey(); + Tuple judgementIndexTuple = parameterJudgement.getVariablesTuple(); + + Object[] judgementVariables = new Object[judgementIndexTuple.getSize()]; + for (int i=0; i + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; + +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.*; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +import java.util.Set; + +public class RepresentativeElectionConstraint extends KeyedEnumerablePConstraint + implements IQueryReference, ITypeInfoProviderConstraint { + private final Connectivity connectivity; + + public RepresentativeElectionConstraint(PBody pBody, Tuple variablesTuple, PQuery supplierKey, + Connectivity connectivity) { + super(pBody, variablesTuple, supplierKey); + this.connectivity = connectivity; + } + + public Connectivity getConnectivity() { + return connectivity; + } + + @Override + public PQuery getReferredQuery() { + return supplierKey; + } + + @Override + public Set getImpliedJudgements(IQueryMetaContext context) { + return PositivePatternCall.getTypesImpliedByCall(supplierKey, variablesTuple); + } + + @Override + protected String keyToString() { + return supplierKey.getFullyQualifiedName() + "#" + connectivity + "#representative"; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java new file mode 100644 index 00000000..2ca54cc0 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/basicenumerables/TypeConstraint.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.basicenumerables; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.KeyedEnumerablePConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; + +/** + * Represents an enumerable type constraint that asserts that values substituted for the given tuple of variables + * form a tuple that belongs to an enumerable extensional relation identified by an {@link IInputKey}. + * + *

The InputKey must be enumerable! + * + * @author Zoltan Ujhelyi + * + */ +public class TypeConstraint extends KeyedEnumerablePConstraint implements ITypeConstraint { + + private TypeJudgement equivalentJudgement; + + public TypeConstraint(PBody pBody, Tuple variablesTuple, IInputKey inputKey) { + super(pBody, variablesTuple, inputKey); + this.equivalentJudgement = new TypeJudgement(inputKey, variablesTuple); + + if (! inputKey.isEnumerable()) + throw new IllegalArgumentException( + this.getClass().getSimpleName() + + " applicable for enumerable input keys only; received instead " + + inputKey); + if (variablesTuple.getSize() != inputKey.getArity()) + throw new IllegalArgumentException( + this.getClass().getSimpleName() + + " applied for variable tuple " + variablesTuple + " having wrong arity for input key " + + inputKey); + } + + @Override + protected String keyToString() { + return supplierKey.getPrettyPrintableName(); + } + + @Override + public TypeJudgement getEquivalentJudgement() { + return equivalentJudgement; + } + + @Override + public Set getImpliedJudgements(IQueryMetaContext context) { + return Collections.singleton(equivalentJudgement); + //return equivalentJudgement.getDirectlyImpliedJudgements(context); + } + + @Override + public Map, Set> getFunctionalDependencies(IQueryMetaContext context) { + return TypeConstraintUtil.getFunctionalDependencies(context, supplierKey, variablesTuple); + } + + @Override + public void doReplaceVariable(PVariable obsolete, PVariable replacement) { + super.doReplaceVariable(obsolete, replacement); + this.equivalentJudgement = new TypeJudgement(getSupplierKey(), variablesTuple); + } +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java new file mode 100644 index 00000000..2c03a894 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/BasePQuery.java @@ -0,0 +1,231 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.queries; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; +import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * Default implementation of PQuery. + * + * @author Bergmann Gabor + */ +public abstract class BasePQuery implements PQuery { + + protected PQueryStatus status = PQueryStatus.UNINITIALIZED; + /** + * @since 2.0 + */ + protected final PVisibility visibility; + protected List pProblems = new ArrayList(); + private List annotations = new ArrayList(); + private QueryEvaluationHint evaluationHints = new QueryEvaluationHint(null, (IQueryBackendFactory)null); + PDisjunction canonicalDisjunction; + private List parameterNames = null; // Lazy initialization + + /** For traceability only. */ + private List wrappingQuerySpecifications = new ArrayList(1); + + @Override + public Integer getPositionOfParameter(String parameterName) { + ensureInitialized(); + int index = getParameterNames().indexOf(parameterName); + return index != -1 ? index : null; + } + + protected void setStatus(PQueryStatus newStatus) { + this.status = newStatus; + } + + protected void addError(PProblem problem) { + status = PQueryStatus.ERROR; + pProblems.add(problem); + } + + @Override + public PQueryStatus getStatus() { + return status; + } + + @Override + public List getPProblems() { + return Collections.unmodifiableList(pProblems); + } + + @Override + public boolean isMutable() { + return status.equals(PQueryStatus.UNINITIALIZED) || status.equals(PQueryStatus.INITIALIZING); + } + + @Override + public void checkMutability() { + Preconditions.checkState(isMutable(), "Cannot edit query definition %s", getFullyQualifiedName()); + } + + /** + * @since 1.5 + */ + public void setEvaluationHints(QueryEvaluationHint hints) { + checkMutability(); + this.evaluationHints = hints; + } + + @Override + public QueryEvaluationHint getEvaluationHints() { + ensureInitialized(); + return evaluationHints; + // TODO instead of field, compute something from annotations? + } + + protected void addAnnotation(PAnnotation annotation) { + checkMutability(); + annotations.add(annotation); + } + + @Override + public List getAllAnnotations() { + ensureInitialized(); + return new ArrayList<>(annotations); + } + + private Stream getAnnotationStreamByName(final String name) { + ensureInitialized(); + return annotations.stream().filter(Objects::nonNull).filter(annotation -> Objects.equals(name, annotation.getName())); + } + + @Override + public List getAnnotationsByName(final String annotationName) { + return getAnnotationStreamByName(annotationName).collect(Collectors.toList()); + } + + @Override + public Optional getFirstAnnotationByName(String annotationName) { + return getAnnotationStreamByName(annotationName).findFirst(); + } + + @Override + public List getParameterNames() { + ensureInitialized(); + if (parameterNames == null) { + parameterNames = getParameters().stream().map(PParameter::getName).collect(Collectors.toList()); + } + return parameterNames; + } + + @Override + public Set getDirectReferredQueries() { + ensureInitialized(); + return canonicalDisjunction.getDirectReferredQueries(); + } + + @Override + public Set getAllReferredQueries() { + ensureInitialized(); + return canonicalDisjunction.getAllReferredQueries(); + } + + + @Override + public List publishedAs() { + return wrappingQuerySpecifications; + } + + @Override + public Set getTypeGuarantees() { + ensureInitialized(); + Set result = new HashSet(); + + List parameters = getParameters(); + for (int i=0; i bodies) { + canonicalDisjunction = new PDisjunction(this, bodies); + for (PBody body : canonicalDisjunction.getBodies()) { + body.setStatus(null); + } + setStatus(PQueryStatus.OK); + } + + /** + * Creates and returns the bodies of the query. If recalled again, a new instance is created. + * + * @return + * @throws ViatraQueryRuntimeException + */ + protected abstract Set doGetContainedBodies(); + + @Override + public String toString() { + return String.format("PQuery<%s>=%s", getFullyQualifiedName(), super.toString()); + } + + /** + * @since 2.0 + */ + @Override + public PVisibility getVisibility() { + return visibility; + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java new file mode 100644 index 00000000..eae4eacf --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PDisjunction.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.queries; + +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.psystem.PBody; + +/** + * + * A disjunction is a set of bodies representing separate conditions. A {@link PQuery} has a single, canonical + * PDisjunction, that can be replaced using rewriter + * + * @author Zoltan Ujhelyi + * + */ +public class PDisjunction { + + private Set bodies; + private PQuery query; + + public PDisjunction(Set bodies) { + this(bodies.iterator().next().getPattern(), bodies); + } + + public PDisjunction(PQuery query, Set bodies) { + super(); + this.query = query; + this.bodies = Collections.unmodifiableSet(new LinkedHashSet<>(bodies)); + this.bodies.forEach(body -> body.setContainerDisjunction(this)); + } + + /** + * Returns an immutable set of bodies that consists of this disjunction + * + * @return the bodies + */ + public Set getBodies() { + return bodies; + } + + /** + * Returns the corresponding query specification. May be null if not set. + */ + public PQuery getQuery() { + return query; + } + + /** + * Returns all queries directly referred in the constraints. They are all required to evaluate this query + * + * @return a non-null, but possibly empty list of query definitions + */ + public Set getDirectReferredQueries() { + return this.getBodies().stream(). + flatMap(PQueries.directlyReferencedQueriesFunction()). // flatten stream of streams + collect(Collectors.toCollection(LinkedHashSet::new)); + } + + /** + * Returns all queries required to evaluate this query (transitively). + * + * @return a non-null, but possibly empty list of query definitions + */ + public Set getAllReferredQueries() { + Set processedQueries = new LinkedHashSet<>(); + processedQueries.add(this.getQuery()); + Set foundQueries = getDirectReferredQueries(); + Set newQueries = new LinkedHashSet<>(foundQueries); + + while(!processedQueries.containsAll(newQueries)) { + PQuery query = newQueries.iterator().next(); + processedQueries.add(query); + newQueries.remove(query); + Set referred = query.getDirectReferredQueries(); + referred.removeAll(processedQueries); + foundQueries.addAll(referred); + newQueries.addAll(referred); + } + return foundQueries; + } + + /** + * Decides whether a disjunction is mutable. A disjunction is mutable if all its contained bodies are mutable. + * + */ + public boolean isMutable() { + for (PBody body : bodies) { + if (!body.isMutable()) { + return false; + } + } + return true; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java new file mode 100644 index 00000000..07165aa2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameter.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.queries; + +import java.util.Objects; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; + +/** + * A descriptor for declared PQuery parameters. A parameter has a name, a declared type and a direction constraint + * + * @author Zoltan Ujhelyi + * + */ +public class PParameter { + + private final String name; + private final String typeName; + private final IInputKey declaredUnaryType; + private final PParameterDirection direction; + + public PParameter(String name) { + this(name, (String) null); + } + + public PParameter(String name, String typeName) { + this(name, typeName, (IInputKey) null); + } + + public PParameter(String name, String typeName, IInputKey declaredUnaryType) { + this(name, typeName, declaredUnaryType, PParameterDirection.INOUT); + } + + /** + * @since 1.4 + */ + public PParameter(String name, String typeName, IInputKey declaredUnaryType, PParameterDirection direction) { + super(); + this.name = name; + this.typeName = typeName; + this.declaredUnaryType = declaredUnaryType; + this.direction = direction; + + if (declaredUnaryType != null && declaredUnaryType.getArity() != 1) { + throw new IllegalArgumentException( + "PParameter declared type must be unary instead of " + declaredUnaryType.getPrettyPrintableName()); + } + } + + /** + * @return the direction + * @since 1.4 + */ + public PParameterDirection getDirection() { + return direction; + } + + /** + * @return the name of the parameter + */ + public String getName() { + return name; + } + + /** + * Returns a textual representation of the declared type of the parameter + * + * @return the type description, or null if not available + */ + public String getTypeName() { + return typeName; + } + + /** + * Yield an {@link IInputKey} representation of the type declared for this parameter. + * + * @return the unary type that was declared on this parameter in the query header, or null if not available + */ + public IInputKey getDeclaredUnaryType() { + return declaredUnaryType; + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PParameter) { + return Objects.equals(name, ((PParameter) obj).name) + && Objects.equals(typeName, ((PParameter) obj).typeName) + && Objects.equals(declaredUnaryType, ((PParameter) obj).declaredUnaryType) + && Objects.equals(direction, ((PParameter) obj).direction); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(name, typeName, declaredUnaryType); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java new file mode 100644 index 00000000..c94d4797 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PParameterDirection.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.queries; + +/** + * Values of this enum describe a constraint to the calling of patterns regarding its parameters. + * + * @author Grill Balázs + * @since 1.4 + * + */ +public enum PParameterDirection { + + /** + * Default value, no additional constraint is applied + */ + INOUT, + + /** + * The parameters marked with this constraints shall be set to a value before calling the pattern + */ + IN, + + /** + * The parameters marked with this constraints shall not be set to a value before calling the pattern + */ + OUT + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java new file mode 100644 index 00000000..1fe4f541 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PProblem.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.queries; + +import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; + +/** + * Represents an error that was detected while the {@link PQuery} object was built from a source. + * @author Bergmann Gabor + * + */ +public class PProblem { + + private final String shortMessage; + private final String location; + private final Exception exception; + + public PProblem(String shortMessage) { + this(null, shortMessage, null, null); + } + /** + * @since 2.0 + */ + public PProblem(String shortMessage, Integer line, Integer column) { + this(null, shortMessage, line, column); + } + public PProblem(QueryProcessingException exception) { + this(exception, exception.getShortMessage(), null, null); + } + public PProblem(Exception exception, String shortMessage) { + this(exception, shortMessage, null, null); + } + + /** + * @since 2.0 + */ + public PProblem(Exception exception, String shortMessage, Integer line, Integer column) { + this.shortMessage = shortMessage; + this.exception = exception; + if (line == null) { + location = "Unspecified location"; + } else if (column == null) { + location = String.format("Line %d", line); + } else { + location = String.format("Line %d Column %d", line, column); + } + } + + public String getShortMessage() { + return shortMessage; + } + public Exception getException() { + return exception; + } + /** + * @since 2.0 + */ + public String getLocation() { + return location; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java new file mode 100644 index 00000000..56f8ca76 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueries.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.queries; + +import java.util.HashSet; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.psystem.IMultiQueryReference; +import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; + +/** + * Utility class for using PQueries in functional/streaming collection operations effectively + * + * @author Zoltan Ujhelyi + * + */ +public final class PQueries { + + /** + * Hidden constructor for utility class + */ + private PQueries() { + } + + /** + * Predicate checking for the status of selected queries + * + */ + public static Predicate queryStatusPredicate(final PQueryStatus status) { + return query -> query.getStatus().equals(status); + } + + /** + * Enumerates referred queries (without duplicates) for the given body + */ + public static Function> directlyReferencedQueriesFunction() { + return body -> (body.getConstraintsOfType(IMultiQueryReference.class).stream() + .flatMap(e -> e.getReferredQueries().stream()).distinct()); + } + + /** + * Enumerates directly referred extensional relations (without duplicates) in the canonical form of the given query + * + * @param enumerablesOnly + * only enumerable type constraints are considered + * @since 2.0 + */ + public static Stream directlyRequiredTypesOfQuery(PQuery query, boolean enumerablesOnly) { + return directlyRequiredTypesOfDisjunction(query.getDisjunctBodies(), enumerablesOnly); + } + + /** + * Enumerates directly referred extensional relations (without duplicates) for the given formulation of a query. + * + * @param enumerablesOnly + * only enumerable type constraints are considered + * @since 2.0 + */ + public static Stream directlyRequiredTypesOfDisjunction(PDisjunction disjunctBodies, + boolean enumerablesOnly) { + Class filterClass = enumerablesOnly ? TypeConstraint.class : ITypeConstraint.class; + return disjunctBodies.getBodies().stream().flatMap(body -> body.getConstraintsOfType(filterClass).stream()) + .map(constraint -> constraint.getEquivalentJudgement().getInputKey()).distinct(); + } + + /** + * @since 1.4 + */ + public static Predicate parameterDirectionPredicate(final PParameterDirection direction) { + return input -> input.getDirection() == direction; + } + + /** + * Returns all {@link PTraceable}s contained in the given {@link PQuery}: itself, its bodies and their constraints. + * + * @since 1.6 + */ + public static Set getTraceables(PQuery query) { + final Set traceables = new HashSet<>(); + traceables.add(query); + query.getDisjunctBodies().getBodies().forEach(body -> { + traceables.add(body); + body.getConstraints().forEach(traceables::add); + }); + return traceables; + } + + /** + * Calculates the simple name related from a given qualified name by finding the part after the last '.' character. + * + * @since 2.0 + */ + public static String calculateSimpleName(String qualifiedName) { + return qualifiedName.substring(qualifiedName.lastIndexOf('.') + 1); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java new file mode 100644 index 00000000..a909c650 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQuery.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.queries; + +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.ViatraQueryRuntimeException; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackend; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendHintProvider; +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; + +/** + * Internal representation of a query / graph pattern (using a constraint system formalism), + * to be interpreted by a query evaluator ({@link IQueryBackend}). + * End-users of VIATRA Query should access a query as an IQuerySpecification instead. + * + *

+ * PQuerys are definitions of queries usable inside pattern descriptions. Such description always has (a non-null) name. The query + * itself is defined as a (non-empty) set of {@link PBody} instances, the result is the disjunction of the single + * {@link PBody} instances.

+ *

+ * A PQuery might be constructed from erroneous patterns or might be uninitialized - this is represented by its status. + * + * @author Zoltan Ujhelyi + * @since 0.8.0 + * @noimplement This interface is not intended to be implemented by clients. Use {@link BasePQuery} as a base class instead. + */ +public interface PQuery extends PQueryHeader, PTraceable { + + // TODO rewritten as / rewritten from traceability to PDisjunction? + + /** + * @author Zoltan Ujhelyi + * + */ + public enum PQueryStatus { + /** + * Marks that the query definition is not initialized + */ + UNINITIALIZED, + /** + * Marks that the query definition is being initialized + * @since 1.4 + */ + INITIALIZING, + /** + * The query definition was successfully initialized + */ + OK, + /** + * The query definition was initialized, but some issues were present + */ + WARNING, + /** + * The query definition was not successfully initialized because of an error + */ + ERROR + } + + /** + * Returns all bodies associated with the query in their canonical form. If called multiple times, the same set with + * the same contents will be returned. + * + */ + PDisjunction getDisjunctBodies(); + + /** + * Returns all queries directly referred in the constraints. They are all required to evaluate this query + * + * @return a non-null, but possibly empty list of query definitions + */ + Set getDirectReferredQueries(); + + /** + * Returns all queries required to evaluate this query (transitively). + * + * @return a non-null, but possibly empty list of query definitions + */ + Set getAllReferredQueries(); + + /** + * Returns the initialization status of the definition + * + */ + PQueryStatus getStatus(); + + /** + * Returns a list describing the problems that were found in this query. + * + *

TODO: formulate invariant connecting {@link #getPProblems()} and {@link #getStatus()}. + * + * @return a non-null, but possibly empty list of problems + */ + List getPProblems(); + + /** + * Before a modification operation is executed, a mutability check is performed (via the {@link #getStatus()} + * implementation, and in case of problems an {@link IllegalStateException} is thrown. + */ + void checkMutability(); + + /** + * An option to check mutability of the query. It can be used to avoid getting an {@link IllegalStateException} by + * the execution of {@link #checkMutability()}. + * + * @return true if the query specification is still editable + */ + boolean isMutable(); + + /** + * Optional hints regarding the query evaluation strategy, to be interpreted by the query engine. + *

To ensure the possibility of external overrides, + * the evaluation engine should not directly consult this field, + * but use an {@link IQueryBackendHintProvider} instead. + */ + public QueryEvaluationHint getEvaluationHints(); + + + /** + * Type information, expressed on query parameters, that all matches of the query are guaranteed to respect. + *

At the very minimum, this should include the declared types of the parameters. + *

The type judgement tuples shall contain the parameter index, NOT the {@link PParameter} object. + * + * @return a non-null set of type judgements that the query guarantees for its matches + */ + public Set getTypeGuarantees(); + + /** + * If the query definition is uninitialized, initializes it. + * @throws ViatraQueryRuntimeException if initialization of query specification fails + */ + public abstract void ensureInitialized(); + + /** + * Returns the end-user query specification API objects that wrap this query. + * + *

Intended for traceability and debug purposes, not part of normal operation. + * Returned list is intended to be appended during query specification construction time. + * + * @return a non-null, but possibly empty list of query specification objects; + */ + List publishedAs(); + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java new file mode 100644 index 00000000..f3671934 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PQueryHeader.java @@ -0,0 +1,101 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.queries; + +import java.util.List; +import java.util.Optional; + +import tools.refinery.viatra.runtime.matchers.psystem.annotations.PAnnotation; + +/** + * Represents header information (metainfo) about a query. + *

To be implemented both by IQuerySpecifications intended for end users, + * and the internal query representation {@link PQuery}. + * + * + * @author Bergmann Gabor + * @since 0.9 + */ +public interface PQueryHeader { + + /** + * Identifies the pattern for which matchers can be instantiated. + */ + public String getFullyQualifiedName(); + + /** + * Return the list of parameter names + * + * @return a non-null, but possibly empty list of parameter names + */ + public List getParameterNames(); + + /** + * Returns a list of parameter descriptions + * + * @return a non-null, but possibly empty list of parameter descriptions + */ + public List getParameters(); + + /** + * Returns the index of a named parameter + * + * @param parameterName + * @return the index, or null of no such parameter is available + */ + public Integer getPositionOfParameter(String parameterName); + + /** + * Returns a parameter by name if exists + * @since 2.1 + */ + default Optional getParameter(String parameterName) { + return Optional.ofNullable(getPositionOfParameter(parameterName)) + .map(getParameters()::get); + } + + /** + * Returns the list of annotations specified for this query + * + * @return a non-null, but possibly empty list of annotations + */ + public List getAllAnnotations(); + + /** + * Returns the list of annotations with a specified name + * + * @param annotationName + * @return a non-null, but possibly empty list of annotations + */ + public List getAnnotationsByName(String annotationName); + + /** + * Returns the first annotation with a specified name + * + * @since 2.0 + */ + public Optional getFirstAnnotationByName(String annotationName); + + /** + * Returns the visibility information about the query. + * @since 2.0 + */ + public PVisibility getVisibility(); + + /** + * Returns the non-qualified name of the query. By default this means returning the qualified name after the last + * '.' character. + * + * @since 2.0 + */ + public default String getSimpleName() { + return PQueries.calculateSimpleName(getFullyQualifiedName()); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java new file mode 100644 index 00000000..7cb312bd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/PVisibility.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.queries; + +/** + * @author Zoltan Ujhelyi + * @since 2.0 + * + */ +public enum PVisibility { + + /** + * A public (default) visibility means a pattern can be called at any time. + */ + PUBLIC, + /** + * A private query is not expected to be called directly, only by a different query matcher. + */ + PRIVATE, + /** + * A query that is only used inside a single caller query and is not visible outside its container query. Such + * patterns must also fulfill the following additional constraints: + * + *

    + *
  • An embedded query must have only a single body.
  • + *
  • An embedded query must not be recursice.
  • + *
+ */ + EMBEDDED + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java new file mode 100644 index 00000000..470d7287 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/queries/QueryInitializationException.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.queries; + +import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; + +/** + * Represent an exception that occurred while initializing the specification of a query. + * @author Bergmann Gabor + * @since 0.9 + * + */ +public class QueryInitializationException extends QueryProcessingException { + + public QueryInitializationException(String message, String[] context, String shortMessage, Object patternDescription, + Throwable cause) { + super(message, context, shortMessage, patternDescription, cause); + } + + public QueryInitializationException(String message, String[] context, String shortMessage, Object patternDescription) { + super(message, context, shortMessage, patternDescription); + } + + private static final long serialVersionUID = 9106033062252951489L; + + + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java new file mode 100644 index 00000000..276b2b42 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/AbstractRewriterTraceSource.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import java.util.Objects; + +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; + +/** + * @since 1.6 + * + */ +public class AbstractRewriterTraceSource { + + private IRewriterTraceCollector traceCollector = NopTraceCollector.INSTANCE; + + public void setTraceCollector(IRewriterTraceCollector traceCollector) { + this.traceCollector = Objects.requireNonNull(traceCollector); + } + + public IPTraceableTraceProvider getTraces() { + return traceCollector; + } + + protected IRewriterTraceCollector getTraceCollector() { + return traceCollector; + } + + /** + * Mark the given derivative to be originated from the given original constraint. + * @since 1.6 + */ + protected void addTrace(PTraceable original, PTraceable derivative){ + traceCollector.addTrace(original, derivative); + } + + /** + * Indicate that the given derivative is removed from the resulting query, thus its trace + * information should be removed also. + * @since 1.6 + */ + protected void derivativeRemoved(PConstraint derivative, IDerivativeModificationReason reason){ + traceCollector.derivativeRemoved(derivative, reason); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java new file mode 100644 index 00000000..237a762d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/ConstraintRemovalReason.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +/** + * Common reasons for removing constraint through rewriters + * + * @noreference This enum is not intended to be referenced by clients. + */ +public enum ConstraintRemovalReason implements IDerivativeModificationReason { + + MOOT_EQUALITY, + WEAK_INEQUALITY_SELF_LOOP, + TYPE_SUBSUMED, + DUPLICATE + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java new file mode 100644 index 00000000..3b5d7390 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/DefaultFlattenCallPredicate.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; + +/** + * @author Marton Bur + * + */ +public class DefaultFlattenCallPredicate implements IFlattenCallPredicate { + + @Override + public boolean shouldFlatten(PositivePatternCall positivePatternCall) { + return true; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java new file mode 100644 index 00000000..06b8d372 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/FlattenerCopier.java @@ -0,0 +1,129 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExpressionEvaluation; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * This rewriter class can add new equality constraints to the copied body + * + * @author Marton Bur + * + */ +class FlattenerCopier extends PBodyCopier { + + private final Map calls; + + private static class CallInformation { + final PBody body; + final Map variableMapping; + + private CallInformation(PBody body) { + this.body = body; + this.variableMapping = new HashMap<>(); + } + } + + public FlattenerCopier(PQuery query, Map callsToFlatten) { + super(query); + this.calls = callsToFlatten.entrySet().stream().collect(Collectors.toMap(Entry::getKey, entry -> new CallInformation(entry.getValue()))); + } + + protected void copyVariable(PositivePatternCall contextPatternCall, PVariable variable, String newName) { + PVariable newPVariable = body.getOrCreateVariableByName(newName); + calls.get(contextPatternCall).variableMapping.put(variable, newPVariable); + variableMapping.put(variable, newPVariable); + } + + /** + * Merge all variables and constraints from the body called through the given pattern call to a target body. If + * multiple bodies are merged into a single one, use the renamer and filter options to avoid collisions. + * + * @param sourceBody + * @param namingTool + * @param filter + */ + public void mergeBody(PositivePatternCall contextPatternCall, IVariableRenamer namingTool, + IConstraintFilter filter) { + + PBody sourceBody = calls.get(contextPatternCall).body; + + // Copy variables + Set allVariables = sourceBody.getAllVariables(); + for (PVariable pVariable : allVariables) { + if (pVariable.isUnique()) { + copyVariable(contextPatternCall, pVariable, + namingTool.createVariableName(pVariable, sourceBody.getPattern())); + } + } + + // Copy constraints which are not filtered + Set constraints = sourceBody.getConstraints(); + for (PConstraint pConstraint : constraints) { + if (!(pConstraint instanceof ExportedParameter) && !filter.filter(pConstraint)) { + copyConstraint(pConstraint); + } + } + } + + @Override + protected void copyPositivePatternCallConstraint(PositivePatternCall positivePatternCall) { + + if (!calls.containsKey(positivePatternCall)) { + // If the call was not flattened, copy the constraint + super.copyPositivePatternCallConstraint(positivePatternCall); + } else { + PBody calledBody = Objects.requireNonNull(calls.get(positivePatternCall).body); + Preconditions.checkArgument(positivePatternCall.getReferredQuery().equals(calledBody.getPattern())); + + List symbolicParameters = calledBody.getSymbolicParameterVariables(); + Object[] elements = positivePatternCall.getVariablesTuple().getElements(); + for (int i = 0; i < elements.length; i++) { + // Create equality constraints between the caller PositivePatternCall and the corresponding body + // parameter variables + createEqualityConstraint((PVariable) elements[i], symbolicParameters.get(i), positivePatternCall); + } + + } + } + + private void createEqualityConstraint(PVariable pVariable1, PVariable pVariable2, + PositivePatternCall contextPatternCall) { + PVariable who = variableMapping.get(pVariable1); + PVariable withWhom = calls.get(contextPatternCall).variableMapping.get(pVariable2); + addTrace(contextPatternCall, new Equality(body, who, withWhom)); + } + + @Override + protected void copyExpressionEvaluationConstraint(final ExpressionEvaluation expressionEvaluation) { + Map variableMapping = this.variableMapping.entrySet().stream() + .filter(input -> expressionEvaluation.getPSystem().getAllVariables().contains(input.getKey())) + .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); + + PVariable mappedOutputVariable = variableMapping.get(expressionEvaluation.getOutputVariable()); + addTrace(expressionEvaluation, new ExpressionEvaluation(body, new VariableMappingExpressionEvaluatorWrapper(expressionEvaluation.getEvaluator(), variableMapping), mappedOutputVariable, expressionEvaluation.isUnwinding())); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java new file mode 100644 index 00000000..518b9c64 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IConstraintFilter.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.ExportedParameter; + +/** + * Helper interface to exclude constraints from PBody copy processes + * + * @author Marton Bur + * + */ +public interface IConstraintFilter { + /** + * Returns true, if the given constraint should be filtered (thus should not be copied) + * + * @param constraint + * to check + * @return true, if the constraint should be filtered + */ + boolean filter(PConstraint constraint); + + public static class ExportedParameterFilter implements IConstraintFilter { + + @Override + public boolean filter(PConstraint constraint) { + return constraint instanceof ExportedParameter; + } + + } + + public static class AllowAllFilter implements IConstraintFilter { + + @Override + public boolean filter(PConstraint constraint) { + // Nothing is filtered + return false; + } + + } +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java new file mode 100644 index 00000000..dbd6a78d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IDerivativeModificationReason.java @@ -0,0 +1,19 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +/** + * This is a role indication interface, implementations may provide a reason about + * why a modification is made during PQuery normalization. + * @since 1.6 + * + */ +public interface IDerivativeModificationReason { + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java new file mode 100644 index 00000000..7e224e98 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IFlattenCallPredicate.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Marton Bur, Zoltan Ujhelyi, Akos Horvath, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; + + +/** + * Interface used by the PQueryFlattener to decide which positive pattern calls to flatten + * + * @author Marton Bur + * + */ +public interface IFlattenCallPredicate { + + /** + * Decides whether the called query by the pattern call should be flattened into the caller or not. + * + * @param positivePatternCall + * the pattern call + * @return true if the call should be flattened + */ + boolean shouldFlatten(PositivePatternCall positivePatternCall); + + /** + * Flattens only if all operand predicates vote for flattening. + * @author Gabor Bergmann + * @since 2.1 + */ + public static class And implements IFlattenCallPredicate { + private IFlattenCallPredicate[] operands; + public And(IFlattenCallPredicate... operands) { + this.operands = operands; + } + + @Override + public boolean shouldFlatten(PositivePatternCall positivePatternCall) { + for (IFlattenCallPredicate operand : operands) { + if (!operand.shouldFlatten(positivePatternCall)) return false; + } + return true; + } + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java new file mode 100644 index 00000000..84da4d1b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IPTraceableTraceProvider.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * This interface provides methods to trace the {@link PTraceable}s of a transformed {@link PQuery} produced by + * a {@link PDisjunctionRewriter}. In case the associated rewriter is a composite (a.k.a. {@link PDisjunctionRewriterCacher}), + * this trace provider handles traces end-to-end, hiding all the intermediate transformation steps. + * + * @since 1.6 + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IPTraceableTraceProvider { + + /** + * Find and return the canonical {@link PTraceable}s in the original query which are the sources of the given derivative + * {@link PTraceable} according to the transformation. + * + * @param derivative a {@link PTraceable} which is contained by the {@link PQuery} produced by the associated rewriter + * @since 2.0 + */ + public Stream getCanonicalTraceables(PTraceable derivative); + + /** + * Find and return the {@link PTraceable}s in the rewritten query which are the destinations of the given source + * {@link PTraceable} according to the transformation. + * + * @param source a {@link PTraceable} which is contained by a {@link PQuery} before rewriting + * @since 2.0 + */ + public Stream getRewrittenTraceables(PTraceable source); + + /** + * Returns whether the given traceable element has been removed by every rewriter for a reason. + */ + public boolean isRemoved(PTraceable traceable); + + /** + * Returns the reasons for which the traceable element has been removed by the rewriters. + * @return the reasons of removal during rewriting + * @since 2.0 + */ + public Stream getRemovalReasons(PTraceable traceable); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java new file mode 100644 index 00000000..70771ea7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IRewriterTraceCollector.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; + +/** + * This is the internal API of {@link IPTraceableTraceProvider} expected to be used by + * copier and rewriter implementations. + * + * @since 1.6 + * @noreference This interface is not intended to be referenced by clients. + */ +public interface IRewriterTraceCollector extends IPTraceableTraceProvider { + + /** + * Mark the given derivative to be originated from the given original constraint. + */ + public void addTrace(PTraceable origin, PTraceable derivative); + + /** + * Indicate that the given derivative is removed from the resulting query, thus its trace + * information should be removed also. + */ + public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java new file mode 100644 index 00000000..ce446e0d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/IVariableRenamer.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Zoltan Ujhelyi, Marton Bur, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * Helper interface to ease the naming of the new variables during flattening + * + * @author Marton Bur + * + */ +public interface IVariableRenamer { + /** + * Creates a variable name based on a given variable and a given query. It only creates a String, doesn't set + * anything. + * + * @param pVariable + * @param query + * @return the new variable name as a String + */ + String createVariableName(PVariable pVariable, PQuery query); + + public class SameName implements IVariableRenamer { + @Override + public String createVariableName(PVariable pVariable, PQuery query) { + return pVariable.getName(); + } + } + + public class HierarchicalName implements IVariableRenamer { + + private int callCount; + + public void setCallCount(int callCount) { + this.callCount = callCount; + } + + @Override + public String createVariableName(PVariable pVariable, PQuery query) { + // make sure to keep the "_" prefix before anonymous variables + String newVarName = getShortName(query) + "<" + callCount + ">" + "_" + pVariable.getName(); + return pVariable.getName().startsWith("_") ? "_" + newVarName : newVarName ; + } + + private String getShortName(PQuery query) { + String fullyQualifiedName = query.getFullyQualifiedName(); + int beginIndex = fullyQualifiedName.lastIndexOf('.') + 1; + return fullyQualifiedName.substring(beginIndex); + } + } +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java new file mode 100644 index 00000000..7429fc60 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/MappingTraceCollector.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * Multimap-based implementation to contain and query traces + * + * @since 1.6 + * + */ +public class MappingTraceCollector implements IRewriterTraceCollector { + + /** + * Traces from derivative to original + */ + private final IMultiLookup traces = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); + + /** + * Traces from original to derivative + */ + private final IMultiLookup inverseTraces = CollectionsFactory.createMultiLookup(Object.class, MemoryType.SETS, Object.class); + + /** + * Reasons for removing {@link PTraceable}s + */ + private final Map removals = new HashMap<>(); + + /** + * Decides whether {@link PTraceable} is removed + */ + private final Predicate removed = removals::containsKey; + + /** + * @since 2.0 + */ + @Override + public Stream getCanonicalTraceables(PTraceable derivative) { + return findTraceEnds(derivative, traces).stream(); + } + + /** + * @since 2.0 + */ + @Override + public Stream getRewrittenTraceables(PTraceable source) { + return findTraceEnds(source, inverseTraces).stream(); + } + + /** + * Returns the end of trace chains starting from the given {@link PTraceable} along the given trace edges. + */ + private Set findTraceEnds(PTraceable traceable, IMultiLookup traceRecords) { + if (traceable instanceof PQuery) { // PQueries are preserved + return Collections.singleton(traceable); + } + Set visited = new HashSet<>(); + Set result = new HashSet<>(); + Queue queue = new LinkedList<>(); + queue.add(traceable); + while(!queue.isEmpty()){ + PTraceable aDerivative = queue.poll(); + // Track visited elements to avoid infinite loop via directed cycles in traces + visited.add(aDerivative); + IMemoryView nextOrigins = traceRecords.lookup(aDerivative); + if (nextOrigins == null){ + // End of trace chain + result.add(aDerivative); + } else { + // Follow traces + for(PTraceable nextOrigin : nextOrigins){ + if (!visited.contains(nextOrigin)){ + queue.add(nextOrigin); + } + } + } + } + return result; + } + + @Override + public void addTrace(PTraceable original, PTraceable derivative){ + traces.addPairOrNop(derivative, original); + inverseTraces.addPairOrNop(original, derivative); + // Even if this element was marked as removed earlier, now we replace it with another constraint! + removals.remove(original); + } + + @Override + public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason){ + Preconditions.checkState(!removals.containsKey(derivative), "Traceable %s removed multiple times", derivative); + // XXX the derivative must not be removed from the trace chain, as some rewriters, e.g. the normalizer keeps trace links to deleted elements + if (!inverseTraces.lookupExists(derivative)) { + // If there already exists a trace link, this removal means an update + removals.put(derivative, reason); + } + } + + @Override + public boolean isRemoved(PTraceable traceable) { + return getRewrittenTraceables(traceable).allMatch(removed); + } + + /** + * @since 2.0 + */ + @Override + public Stream getRemovalReasons(PTraceable traceable) { + return getRewrittenTraceables(traceable).filter(removed).map(removals::get); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java new file mode 100644 index 00000000..96c0b205 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NeverFlattenCallPredicate.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; + +/** + * @author Grill Balázs + * @since 1.4 + * + */ +public class NeverFlattenCallPredicate implements IFlattenCallPredicate { + + + @Override + public boolean shouldFlatten(PositivePatternCall positivePatternCall) { + return false; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java new file mode 100644 index 00000000..15cf577e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/NopTraceCollector.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Grill Balázs, IncQueryLabs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.psystem.PTraceable; + +/** + * This implementation does not store any traces and scales to NOP for every traceability feature. + * @since 1.6 + * + */ +public class NopTraceCollector implements IRewriterTraceCollector { + + public static final IRewriterTraceCollector INSTANCE = new NopTraceCollector(); + + private NopTraceCollector() { + // Private constructor to force using the common instance + } + + /** + * @since 2.0 + */ + @Override + public Stream getCanonicalTraceables(PTraceable derivative) { + return Stream.empty(); + } + + /** + * @since 2.0 + */ + @Override + public Stream getRewrittenTraceables(PTraceable source) { + return Stream.empty(); + } + + + @Override + public void addTrace(PTraceable origin, PTraceable derivative) { + // ignored + } + + @Override + public void derivativeRemoved(PTraceable derivative, IDerivativeModificationReason reason) { + // ignored + } + + @Override + public boolean isRemoved(PTraceable traceable) { + return false; + } + + /** + * @since 2.0 + */ + @Override + public Stream getRemovalReasons(PTraceable traceable) { + return Stream.empty(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java new file mode 100644 index 00000000..e66c4eea --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java @@ -0,0 +1,306 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; +import tools.refinery.viatra.runtime.matchers.psystem.EnumerablePConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.*; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.*; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PParameter; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter; +import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * This class can create a new PBody for a PQuery. The result body contains a copy of given variables and constraints. + * + * @author Marton Bur + * + */ +public class PBodyCopier extends AbstractRewriterTraceSource { + + /** + * The created body + */ + protected PBody body; + /** + * Mapping between the original and the copied variables + */ + protected Map variableMapping = new HashMap<>(); + + public Map getVariableMapping() { + return variableMapping; + } + + /** + * @since 1.6 + */ + public PBodyCopier(PBody body, IRewriterTraceCollector traceCollector) { + this.body = new PBody(body.getPattern()); + setTraceCollector(traceCollector); + + // do the actual copying + mergeBody(body); + } + + /** + * @since 1.6 + */ + public PBodyCopier(PQuery query) { + this.body = new PBody(query); + } + + public void mergeBody(PBody sourceBody) { + mergeBody(sourceBody, new SameName(), new AllowAllFilter()); + } + + /** + * Merge all variables and constraints from a source body to a target body. If multiple bodies are merged into a + * single one, use the renamer and filter options to avoid collisions. + */ + public void mergeBody(PBody sourceBody, IVariableRenamer namingTool, IConstraintFilter filter) { + + // Copy variables + Set allVariables = sourceBody.getAllVariables(); + for (PVariable pVariable : allVariables) { + if (pVariable.isUnique()) { + copyVariable(pVariable, namingTool.createVariableName(pVariable, sourceBody.getPattern())); + } + } + + // Copy exported parameters + this.body.setSymbolicParameters(sourceBody.getSymbolicParameters().stream() + .map(this::copyExportedParameterConstraint).collect(Collectors.toList())); + + // Copy constraints which are not filtered + Set constraints = sourceBody.getConstraints(); + for (PConstraint pConstraint : constraints) { + if (!(pConstraint instanceof ExportedParameter) && !filter.filter(pConstraint)) { + copyConstraint(pConstraint); + } + } + + // Add trace between original and copied body + addTrace(sourceBody, body); + } + + protected void copyVariable(PVariable variable, String newName) { + PVariable newPVariable = body.getOrCreateVariableByName(newName); + variableMapping.put(variable, newPVariable); + } + + /** + * Returns the body with the copied variables and constraints. The returned body is still uninitialized. + */ + public PBody getCopiedBody() { + return body; + } + + protected void copyConstraint(PConstraint constraint) { + if (constraint instanceof ExportedParameter) { + copyExportedParameterConstraint((ExportedParameter) constraint); + } else if (constraint instanceof Equality) { + copyEqualityConstraint((Equality) constraint); + } else if (constraint instanceof Inequality) { + copyInequalityConstraint((Inequality) constraint); + } else if (constraint instanceof TypeConstraint) { + copyTypeConstraint((TypeConstraint) constraint); + } else if (constraint instanceof TypeFilterConstraint) { + copyTypeFilterConstraint((TypeFilterConstraint) constraint); + } else if (constraint instanceof ConstantValue) { + copyConstantValueConstraint((ConstantValue) constraint); + } else if (constraint instanceof PositivePatternCall) { + copyPositivePatternCallConstraint((PositivePatternCall) constraint); + } else if (constraint instanceof NegativePatternCall) { + copyNegativePatternCallConstraint((NegativePatternCall) constraint); + } else if (constraint instanceof BinaryTransitiveClosure) { + copyBinaryTransitiveClosureConstraint((BinaryTransitiveClosure) constraint); + } else if (constraint instanceof RepresentativeElectionConstraint) { + copyRepresentativeElectionConstraint((RepresentativeElectionConstraint) constraint); + } else if (constraint instanceof RelationEvaluation) { + copyRelationEvaluationConstraint((RelationEvaluation) constraint); + } else if (constraint instanceof BinaryReflexiveTransitiveClosure) { + copyBinaryReflexiveTransitiveClosureConstraint((BinaryReflexiveTransitiveClosure) constraint); + } else if (constraint instanceof PatternMatchCounter) { + copyPatternMatchCounterConstraint((PatternMatchCounter) constraint); + } else if (constraint instanceof AggregatorConstraint) { + copyAggregatorConstraint((AggregatorConstraint) constraint); + } else if (constraint instanceof ExpressionEvaluation) { + copyExpressionEvaluationConstraint((ExpressionEvaluation) constraint); + } else { + throw new QueryProcessingException("Unknown PConstraint {0} encountered while copying PBody", + new String[] { constraint.getClass().getName() }, "Unknown PConstraint", body.getPattern()); + } + } + + protected ExportedParameter copyExportedParameterConstraint(ExportedParameter exportedParameter) { + PVariable mappedPVariable = variableMapping.get(exportedParameter.getParameterVariable()); + PParameter parameter = exportedParameter.getPatternParameter(); + ExportedParameter newExportedParameter; + newExportedParameter = new ExportedParameter(body, mappedPVariable, parameter); + body.getSymbolicParameters().add(newExportedParameter); + addTrace(exportedParameter, newExportedParameter); + return newExportedParameter; + } + + protected void copyEqualityConstraint(Equality equality) { + PVariable who = equality.getWho(); + PVariable withWhom = equality.getWithWhom(); + addTrace(equality, new Equality(body, variableMapping.get(who), variableMapping.get(withWhom))); + } + + protected void copyInequalityConstraint(Inequality inequality) { + PVariable who = inequality.getWho(); + PVariable withWhom = inequality.getWithWhom(); + addTrace(inequality, new Inequality(body, variableMapping.get(who), variableMapping.get(withWhom))); + } + + protected void copyTypeConstraint(TypeConstraint typeConstraint) { + PVariable[] mappedVariables = extractMappedVariables(typeConstraint); + Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); + addTrace(typeConstraint, new TypeConstraint(body, variablesTuple, typeConstraint.getSupplierKey())); + } + + protected void copyTypeFilterConstraint(TypeFilterConstraint typeConstraint) { + PVariable[] mappedVariables = extractMappedVariables(typeConstraint); + Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); + addTrace(typeConstraint, new TypeFilterConstraint(body, variablesTuple, typeConstraint.getInputKey())); + } + + protected void copyConstantValueConstraint(ConstantValue constantValue) { + PVariable pVariable = (PVariable) constantValue.getVariablesTuple().getElements()[0]; + addTrace(constantValue, + new ConstantValue(body, variableMapping.get(pVariable), constantValue.getSupplierKey())); + } + + protected void copyPositivePatternCallConstraint(PositivePatternCall positivePatternCall) { + PVariable[] mappedVariables = extractMappedVariables(positivePatternCall); + Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); + addTrace(positivePatternCall, + new PositivePatternCall(body, variablesTuple, positivePatternCall.getReferredQuery())); + } + + protected void copyNegativePatternCallConstraint(NegativePatternCall negativePatternCall) { + PVariable[] mappedVariables = extractMappedVariables(negativePatternCall); + Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); + addTrace(negativePatternCall, + new NegativePatternCall(body, variablesTuple, negativePatternCall.getReferredQuery())); + } + + protected void copyBinaryTransitiveClosureConstraint(BinaryTransitiveClosure binaryTransitiveClosure) { + PVariable[] mappedVariables = extractMappedVariables(binaryTransitiveClosure); + Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); + addTrace(binaryTransitiveClosure, + new BinaryTransitiveClosure(body, variablesTuple, binaryTransitiveClosure.getReferredQuery())); + } + + protected void copyRepresentativeElectionConstraint(RepresentativeElectionConstraint constraint) { + var mappedVariables = extractMappedVariables(constraint); + var variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); + addTrace(constraint, new RepresentativeElectionConstraint(body, variablesTuple, constraint.getReferredQuery(), + constraint.getConnectivity())); + } + + /** + * @since 2.8 + */ + protected void copyRelationEvaluationConstraint(RelationEvaluation relationEvaluation) { + PVariable[] mappedVariables = extractMappedVariables(relationEvaluation); + Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); + addTrace(relationEvaluation, new RelationEvaluation(body, variablesTuple, relationEvaluation.getReferredQueries(), + relationEvaluation.getEvaluator())); + } + + /** + * @since 2.0 + */ + protected void copyBinaryReflexiveTransitiveClosureConstraint( + BinaryReflexiveTransitiveClosure binaryReflexiveTransitiveClosure) { + PVariable[] mappedVariables = extractMappedVariables(binaryReflexiveTransitiveClosure); + Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); + addTrace(binaryReflexiveTransitiveClosure, + new BinaryReflexiveTransitiveClosure(body, variablesTuple, + binaryReflexiveTransitiveClosure.getReferredQuery(), + binaryReflexiveTransitiveClosure.getUniverseType())); + } + + protected void copyPatternMatchCounterConstraint(PatternMatchCounter patternMatchCounter) { + PVariable[] mappedVariables = extractMappedVariables(patternMatchCounter); + PVariable mappedResultVariable = variableMapping.get(patternMatchCounter.getResultVariable()); + Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); + addTrace(patternMatchCounter, new PatternMatchCounter(body, variablesTuple, + patternMatchCounter.getReferredQuery(), mappedResultVariable)); + } + + /** + * @since 1.4 + */ + protected void copyAggregatorConstraint(AggregatorConstraint constraint) { + PVariable[] mappedVariables = extractMappedVariables(constraint); + PVariable mappedResultVariable = variableMapping.get(constraint.getResultVariable()); + Tuple variablesTuple = Tuples.flatTupleOf((Object[]) mappedVariables); + addTrace(constraint, new AggregatorConstraint(constraint.getAggregator(), body, variablesTuple, + constraint.getReferredQuery(), mappedResultVariable, constraint.getAggregatedColumn())); + } + + protected void copyExpressionEvaluationConstraint(ExpressionEvaluation expressionEvaluation) { + PVariable mappedOutputVariable = variableMapping.get(expressionEvaluation.getOutputVariable()); + addTrace(expressionEvaluation, new ExpressionEvaluation(body, + new VariableMappingExpressionEvaluatorWrapper(expressionEvaluation.getEvaluator(), variableMapping), + mappedOutputVariable, expressionEvaluation.isUnwinding())); + } + + /** + * For positive pattern calls + * + * @param positivePatternCall + * @return the mapped variables to the pattern's parameters + */ + protected PVariable[] extractMappedVariables(EnumerablePConstraint enumerablePConstraint) { + Object[] pVariables = enumerablePConstraint.getVariablesTuple().getElements(); + return mapVariableList(pVariables); + } + + /** + * For negative and count pattern calls. + * + * @param patternMatchCounter + * @return the mapped variables to the pattern's parameters + */ + private PVariable[] extractMappedVariables(PatternCallBasedDeferred patternCallBasedDeferred) { + Object[] pVariables = patternCallBasedDeferred.getActualParametersTuple().getElements(); + return mapVariableList(pVariables); + } + + /** + * For type filters. + */ + private PVariable[] extractMappedVariables(TypeFilterConstraint typeFilterConstraint) { + Object[] pVariables = typeFilterConstraint.getVariablesTuple().getElements(); + return mapVariableList(pVariables); + } + + private PVariable[] mapVariableList(Object[] pVariables) { + List list = new ArrayList(); + for (int i = 0; i < pVariables.length; i++) { + PVariable mappedVariable = variableMapping.get(pVariables[i]); + list.add(mappedVariable); + } + return list.toArray(new PVariable[0]); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java new file mode 100644 index 00000000..90943129 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyNormalizer.java @@ -0,0 +1,310 @@ +/******************************************************************************* + * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.IQueryMetaContext; +import tools.refinery.viatra.runtime.matchers.planning.QueryProcessingException; +import tools.refinery.viatra.runtime.matchers.planning.helpers.TypeHelper; +import tools.refinery.viatra.runtime.matchers.psystem.ITypeConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.ITypeInfoProviderConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.TypeJudgement; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Equality; +import tools.refinery.viatra.runtime.matchers.psystem.basicdeferred.Inequality; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; + +/** + * A disjunction rewriter for creating a normalized form of specification, unifying variables and running basic sanity + * checks. This rewriter does not copy but modifies directly the original specification, requiring a mutable + * disjunction. + * + * @author Gabor Bergmann + * + */ +public class PBodyNormalizer extends PDisjunctionRewriter { + + private IQueryMetaContext context; + + public PBodyNormalizer(IQueryMetaContext context) { + this.context = context; + } + + /** + * Returns whether unary constraint elimination is enabled. This behavior can be customized by creating a subclass + * with a custom implementation. + * + * @since 1.6 + */ + protected boolean shouldCalculateImpliedTypes(PQuery query) { + return true; + } + + /** + * Returns whether 'weakened alternative' suggestions of the context shall be expanded as additional PConstraints. + * This behavior can be customized by creating a subclass + * with a custom implementation. + * + * @since 1.6 + */ + protected boolean shouldExpandWeakenedAlternatives(PQuery query) { + return false; + } + + @Override + public PDisjunction rewrite(PDisjunction disjunction) { + Set normalizedBodies = new LinkedHashSet<>(); + for (PBody body : disjunction.getBodies()) { + PBodyCopier copier = new PBodyCopier(body, getTraceCollector()); + PBody modifiedBody = copier.getCopiedBody(); + normalizeBody(modifiedBody); + normalizedBodies.add(modifiedBody); + modifiedBody.setStatus(PQueryStatus.OK); + } + return new PDisjunction(normalizedBodies); + } + + public void setContext(IQueryMetaContext context) { + this.context = context; + } + + /** + * Provides a normalized version of the pattern body. May return a different version than the original version if + * needed. + * + * @param body + */ + public PBody normalizeBody(PBody body) { + try { + return normalizeBodyInternal(body); + } catch (QueryProcessingException e) { + throw new RewriterException("Error during rewriting: {1}", new String[] { e.getMessage() }, + e.getShortMessage(), body.getPattern(), e); + } + } + + PBody normalizeBodyInternal(PBody body) { + // UNIFICATION AND WEAK INEQUALITY ELIMINATION + unifyVariablesAlongEqualities(body); + eliminateWeakInequalities(body); + removeMootEqualities(body); + + // ADDING WEAKENED ALTERNATIVES + if (shouldExpandWeakenedAlternatives(body.getPattern())) { + expandWeakenedAlternativeConstraints(body); + } + + // CONSTRAINT ELIMINATION WITH TYPE INFERENCE + if (shouldCalculateImpliedTypes(body.getPattern())) { + eliminateInferrableTypes(body, context); + } else { + // ELIMINATE DUPLICATE TYPE CONSTRAINTS + eliminateDuplicateTypeConstraints(body); + } + + + // PREVENTIVE CHECKS + checkSanity(body); + return body; + } + + private void removeMootEqualities(PBody body) { + Set equals = body.getConstraintsOfType(Equality.class); + for (Equality equality : equals) { + if (equality.isMoot()) { + equality.delete(); + derivativeRemoved(equality, ConstraintRemovalReason.MOOT_EQUALITY); + } + } + } + + /** + * Unifies allVariables along equalities so that they can be handled as one. + * + * @param body + */ + void unifyVariablesAlongEqualities(PBody body) { + Set equals = body.getConstraintsOfType(Equality.class); + for (Equality equality : equals) { + if (!equality.isMoot()) { + equality.getWho().unifyInto(equality.getWithWhom()); + } + } + } + + /** + * Eliminates weak inequalities if they are not substantiated. + * + * @param body + */ + void eliminateWeakInequalities(PBody body) { + for (Inequality inequality : body.getConstraintsOfType(Inequality.class)){ + if (inequality.isEliminable()){ + inequality.eliminateWeak(); + derivativeRemoved(inequality, ConstraintRemovalReason.WEAK_INEQUALITY_SELF_LOOP); + } + } + } + + /** + * Eliminates all type constraints that are inferrable from other constraints. + */ + void eliminateInferrableTypes(final PBody body, IQueryMetaContext context) { + Set subsumedByRetainedConstraints = new HashSet(); + LinkedList allTypeConstraints = new LinkedList(); + for (PConstraint pConstraint : body.getConstraints()) { + if (pConstraint instanceof ITypeConstraint) { + allTypeConstraints.add((ITypeConstraint) pConstraint); + } else if (pConstraint instanceof ITypeInfoProviderConstraint) { + // non-type constraints are all retained + final Set directJudgements = ((ITypeInfoProviderConstraint) pConstraint) + .getImpliedJudgements(context); + subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints, directJudgements, + context); + } + } + Comparator eliminationOrder = (o1, o2) -> { + IInputKey type1 = o1.getEquivalentJudgement().getInputKey(); + IInputKey type2 = o2.getEquivalentJudgement().getInputKey(); + + int result = context.getSuggestedEliminationOrdering().compare(type1, type2); + return (result == 0) + ? PConstraint.COMPARE_BY_MONOTONOUS_ID.compare(o1, o2) + : result; + }; + + Collections.sort(allTypeConstraints, eliminationOrder); + Queue potentialConstraints = allTypeConstraints; // rename for better comprehension + + while (!potentialConstraints.isEmpty()) { + ITypeConstraint candidate = potentialConstraints.poll(); + + boolean isSubsumed = subsumedByRetainedConstraints.contains(candidate.getEquivalentJudgement()); + if (!isSubsumed) { + Set typeClosure = subsumedByRetainedConstraints; + for (ITypeConstraint subsuming : potentialConstraints) { // the remaining ones + final Set directJudgements = subsuming.getImpliedJudgements(context); + typeClosure = TypeHelper.typeClosure(typeClosure, directJudgements, context); + + if (typeClosure.contains(candidate.getEquivalentJudgement())) { + isSubsumed = true; + break; + } + } + } + if (isSubsumed) { // eliminated + candidate.delete(); + derivativeRemoved(candidate, ConstraintRemovalReason.TYPE_SUBSUMED); + } else { // retained + subsumedByRetainedConstraints = TypeHelper.typeClosure(subsumedByRetainedConstraints, + candidate.getImpliedJudgements(context), context); + } + } + } + + /** + * Inserts "weakened alternative" constraints suggested by the meta context that aid in coming up with a query plan. + */ + void expandWeakenedAlternativeConstraints(PBody body) { + Set allJudgements = new HashSet(); + Set newJudgementsToAdd = new HashSet(); + Queue judgementsToProcess = new LinkedList(); + Map> traceability = CollectionsFactory.createMap(); + + for (ITypeConstraint typeConstraint : body.getConstraintsOfType(ITypeConstraint.class)) { + TypeJudgement equivalentJudgement = typeConstraint.getEquivalentJudgement(); + judgementsToProcess.add(equivalentJudgement); + allJudgements.add(equivalentJudgement); + traceability.computeIfAbsent(equivalentJudgement, k-> new ArrayList<>()).add(typeConstraint); + } + + while (!judgementsToProcess.isEmpty()) { + TypeJudgement judgement = judgementsToProcess.poll(); + for (TypeJudgement alternativeJudgement : judgement.getWeakenedAlternativeJudgements(context)) { + if (allJudgements.add(alternativeJudgement)) { + newJudgementsToAdd.add(alternativeJudgement); + judgementsToProcess.add(alternativeJudgement); + traceability.merge( + alternativeJudgement, + traceability.getOrDefault(judgement, new ArrayList<>()), + (old,further) -> {old.addAll(further); return old;} + ); + } + } + } + + for (TypeJudgement typeJudgement : newJudgementsToAdd) { + PConstraint newConstraint = typeJudgement.createConstraintFor(body); + for (PConstraint source : traceability.getOrDefault(typeJudgement, Collections.emptyList())) { + addTrace(source, newConstraint); + } + } + } + + private Object getConstraintKey(PConstraint constraint) { + if (constraint instanceof ITypeConstraint) { + return ((ITypeConstraint) constraint).getEquivalentJudgement(); + } + // Do not check duplication for any other types + return constraint; + } + + void eliminateDuplicateTypeConstraints(PBody body) { + Map constraints = new HashMap<>(); + for (PConstraint constraint : body.getConstraints()) { + Object key = getConstraintKey(constraint); + // Retain first found instance of a constraint + if (!constraints.containsKey(key)) { + constraints.put(key, constraint); + } + } + + // Retain collected constraints, remove everything else + Iterator iterator = body.getConstraints().iterator(); + Collection toRetain = constraints.values(); + while(iterator.hasNext()){ + PConstraint next = iterator.next(); + if (!toRetain.contains(next)){ + derivativeRemoved(next, ConstraintRemovalReason.DUPLICATE); + iterator.remove(); + } + } + } + + /** + * Verifies the sanity of all constraints. Should be issued as a preventive check before layouting. + * + * @param body + * @throws RetePatternBuildException + */ + void checkSanity(PBody body) { + for (PConstraint pConstraint : body.getConstraints()) + pConstraint.checkSanity(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java new file mode 100644 index 00000000..c844ccf7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriter.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * An abstract base class for creating alternative representations for PDisjunctions. + * @author Zoltan Ujhelyi + * + */ +public abstract class PDisjunctionRewriter extends AbstractRewriterTraceSource{ + + public abstract PDisjunction rewrite(PDisjunction disjunction); + + public PDisjunction rewrite(PQuery query) { + return rewrite(query.getDisjunctBodies()); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java new file mode 100644 index 00000000..eb5422ca --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PDisjunctionRewriterCacher.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.WeakHashMap; + +import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; + +/** + * A rewriter that stores the previously computed results of a rewriter or a rewriter chain. + * + * @author Zoltan Ujhelyi + * @since 1.0 + */ +public class PDisjunctionRewriterCacher extends PDisjunctionRewriter { + + private final List rewriterChain; + private WeakHashMap cachedResults = + new WeakHashMap(); + + private void setupTraceCollectorInChain(){ + IRewriterTraceCollector collector = getTraceCollector(); + for(PDisjunctionRewriter rewriter: rewriterChain){ + rewriter.setTraceCollector(collector); + } + } + + public PDisjunctionRewriterCacher(PDisjunctionRewriter rewriter) { + rewriterChain = Collections.singletonList(rewriter); + } + + public PDisjunctionRewriterCacher(PDisjunctionRewriter... rewriters) { + rewriterChain = new ArrayList<>(Arrays.asList(rewriters)); + } + + public PDisjunctionRewriterCacher(List rewriterChain) { + this.rewriterChain = new ArrayList<>(rewriterChain); + } + + @Override + public PDisjunction rewrite(PDisjunction disjunction) { + if (!cachedResults.containsKey(disjunction)) { + PDisjunction rewritten = disjunction; + setupTraceCollectorInChain(); + for (PDisjunctionRewriter rewriter : rewriterChain) { + rewritten = rewriter.rewrite(rewritten); + } + + cachedResults.put(disjunction, rewritten); + } + return cachedResults.get(disjunction); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java new file mode 100644 index 00000000..76311d8f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PQueryFlattener.java @@ -0,0 +1,253 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; +import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.AllowAllFilter; +import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IConstraintFilter.ExportedParameterFilter; +import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.HierarchicalName; +import tools.refinery.viatra.runtime.matchers.psystem.rewriters.IVariableRenamer.SameName; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; +import tools.refinery.viatra.runtime.matchers.util.Sets; + +/** + * This rewriter class holds the query flattening logic + * + * @author Marton Bur + * + */ +public class PQueryFlattener extends PDisjunctionRewriter { + + /** + * Utility function to produce the permutation of every possible mapping of values. + * + * @param values + * @return + */ + private static Set> permutation(Map> values) { + // An ordering of keys is defined here which will help restoring the appropriate values after the execution of + // the cartesian product + List keyList = new ArrayList<>(values.keySet()); + + // Produce list of value sets with the ordering defined by keyList + List> valuesList = new ArrayList>(keyList.size()); + for (K key : keyList) { + valuesList.add(values.get(key)); + } + + // Cartesian product will obey ordering of the list + Set> valueMappings = Sets.cartesianProduct(valuesList); + + // Build result + Set> result = new LinkedHashSet<>(); + for (List valueList : valueMappings) { + Map map = new HashMap<>(); + for (int i = 0; i < keyList.size(); i++) { + map.put(keyList.get(i), valueList.get(i)); + } + result.add(map); + } + + return result; + } + + private IFlattenCallPredicate flattenCallPredicate; + + public PQueryFlattener(IFlattenCallPredicate flattenCallPredicate) { + this.flattenCallPredicate = flattenCallPredicate; + } + + @Override + public PDisjunction rewrite(PDisjunction disjunction) { + PQuery query = disjunction.getQuery(); + + // Check for recursion + Set allReferredQueries = disjunction.getAllReferredQueries(); + for (PQuery referredQuery : allReferredQueries) { + if (referredQuery.getAllReferredQueries().contains(referredQuery)) { + throw new RewriterException("Recursive queries are not supported, can't flatten query named \"{1}\"", + new String[] { query.getFullyQualifiedName() }, "Unsupported recursive query", query); + } + } + + return this.doFlatten(disjunction); + } + + /** + * Return the list of dependencies (including the root) in chronological order + * + * @param rootDisjunction + * @return + */ + private List disjunctionDependencies(PDisjunction rootDisjunction) { + // Disjunctions are first collected into a list usign a depth-first approach, + // which can be iterated backwards while removing duplicates + Deque stack = new ArrayDeque<>(); + LinkedList list = new LinkedList<>(); + stack.push(rootDisjunction); + list.add(rootDisjunction); + + while (!stack.isEmpty()) { + PDisjunction disjunction = stack.pop(); + // Collect dependencies + for (PBody pBody : disjunction.getBodies()) { + for (PConstraint constraint : pBody.getConstraints()) { + if (constraint instanceof PositivePatternCall) { + PositivePatternCall positivePatternCall = (PositivePatternCall) constraint; + if (flattenCallPredicate.shouldFlatten(positivePatternCall)) { + // If the above preconditions meet, the call should be flattened + PDisjunction calledDisjunction = positivePatternCall.getReferredQuery().getDisjunctBodies(); + stack.push(calledDisjunction); + list.add(calledDisjunction); + } + } + } + } + } + + // Remove duplicates (keeping the last instance) and reverse order + Set visited = new HashSet(); + List result = new ArrayList(list.size()); + + list.descendingIterator().forEachRemaining(item -> { + if (!visited.contains(item)) { + result.add(item); + visited.add(item); + } + + }); + + return result; + } + + /** + * This function holds the actual flattening logic for a PQuery + * + * @param rootDisjunction + * to be flattened + * @return the flattened bodies of the pQuery + */ + private PDisjunction doFlatten(PDisjunction rootDisjunction) { + + Map> flatBodyMapping = new HashMap<>(); + + List dependencies = disjunctionDependencies(rootDisjunction); + + for (PDisjunction disjunction : dependencies) { + Set flatBodies = new LinkedHashSet<>(); + for (PBody body : disjunction.getBodies()) { + if (isFlatteningNeeded(body)) { + Map> flattenedBodies = new HashMap<>(); + for (PConstraint pConstraint : body.getConstraints()) { + + if (pConstraint instanceof PositivePatternCall) { + PositivePatternCall positivePatternCall = (PositivePatternCall) pConstraint; + if (flattenCallPredicate.shouldFlatten(positivePatternCall)) { + // If the above preconditions meet, do the flattening and return the disjoint bodies + PDisjunction calledDisjunction = positivePatternCall.getReferredQuery() + .getDisjunctBodies(); + + Set flattenedBodySet = flatBodyMapping.get(calledDisjunction); + Preconditions.checkArgument(!flattenedBodySet.isEmpty()); + flattenedBodies.put(positivePatternCall, flattenedBodySet); + } + } + } + flatBodies.addAll(createSetOfFlatPBodies(body, flattenedBodies)); + } else { + flatBodies.add(prepareFlatPBody(body)); + } + } + flatBodyMapping.put(disjunction, flatBodies); + } + + return new PDisjunction(rootDisjunction.getQuery(), flatBodyMapping.get(rootDisjunction)); + } + + /** + * Creates the flattened bodies based on the caller body and the called (and already flattened) disjunctions + * + * @param pBody + * the body to flatten + * @param flattenedDisjunctions + * the + * @param flattenedCalls + * @return + */ + private Set createSetOfFlatPBodies(PBody pBody, Map> flattenedCalls) { + PQuery pQuery = pBody.getPattern(); + + Set> conjunctedCalls = permutation(flattenedCalls); + + // The result set containing the merged conjuncted bodies + Set conjunctedBodies = new HashSet<>(); + + for (Map calledBodies : conjunctedCalls) { + FlattenerCopier copier = createBodyCopier(pQuery, calledBodies); + + int i = 0; + HierarchicalName hierarchicalNamingTool = new HierarchicalName(); + for (PositivePatternCall patternCall : calledBodies.keySet()) { + // Merge each called body + hierarchicalNamingTool.setCallCount(i++); + copier.mergeBody(patternCall, hierarchicalNamingTool, new ExportedParameterFilter()); + } + + // Merge the caller's constraints to the conjunct body + copier.mergeBody(pBody); + + PBody copiedBody = copier.getCopiedBody(); + copiedBody.setStatus(PQueryStatus.OK); + conjunctedBodies.add(copiedBody); + } + + return conjunctedBodies; + } + + private FlattenerCopier createBodyCopier(PQuery query, Map calledBodies) { + FlattenerCopier flattenerCopier = new FlattenerCopier(query, calledBodies); + flattenerCopier.setTraceCollector(getTraceCollector()); + return flattenerCopier; + } + + private PBody prepareFlatPBody(PBody pBody) { + PBodyCopier copier = createBodyCopier(pBody.getPattern(), Collections. emptyMap()); + copier.mergeBody(pBody, new SameName(), new AllowAllFilter()); + // the copying of the body here is necessary for only one containing PDisjunction can be assigned to a PBody + return copier.getCopiedBody(); + } + + private boolean isFlatteningNeeded(PBody pBody) { + // Check if the body contains positive pattern call AND if it should be flattened + for (PConstraint pConstraint : pBody.getConstraints()) { + if (pConstraint instanceof PositivePatternCall) { + return flattenCallPredicate.shouldFlatten((PositivePatternCall) pConstraint); + } + } + return false; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java new file mode 100644 index 00000000..d0fc286b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/RewriterException.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2010-2014, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import tools.refinery.viatra.runtime.matchers.psystem.queries.QueryInitializationException; + +/** + * An exception to wrap various issues during PDisjunction rewriting. + * @author Zoltan Ujhelyi + * + */ +public class RewriterException extends QueryInitializationException { + + private static final long serialVersionUID = -4703825954995497932L; + + public RewriterException(String message, String[] context, String shortMessage, Object patternDescription, + Throwable cause) { + super(message, context, shortMessage, patternDescription, cause); + } + + public RewriterException(String message, String[] context, String shortMessage, Object patternDescription) { + super(message, context, shortMessage, patternDescription); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java new file mode 100644 index 00000000..71459558 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/SurrogateQueryRewriter.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import java.util.LinkedHashSet; +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.context.IInputKey; +import tools.refinery.viatra.runtime.matchers.context.surrogate.SurrogateQueryRegistry; +import tools.refinery.viatra.runtime.matchers.psystem.PBody; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.PositivePatternCall; +import tools.refinery.viatra.runtime.matchers.psystem.basicenumerables.TypeConstraint; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PDisjunction; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery.PQueryStatus; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuples; + +/** + * @author Zoltan Ujhelyi + * + */ +public class SurrogateQueryRewriter extends PDisjunctionRewriter { + + @Override + public PDisjunction rewrite(PDisjunction disjunction) { + Set replacedBodies = new LinkedHashSet<>(); + for (PBody body : disjunction.getBodies()) { + PBodyCopier copier = new PBodyCopier(body, getTraceCollector()) { + + @Override + protected void copyTypeConstraint(TypeConstraint typeConstraint) { + PVariable[] mappedVariables = extractMappedVariables(typeConstraint); + Tuple variablesTuple = Tuples.flatTupleOf((Object[])mappedVariables); + final IInputKey supplierKey = typeConstraint.getSupplierKey(); + if(SurrogateQueryRegistry.instance().hasSurrogateQueryFQN(supplierKey)) { + PQuery surrogateQuery = SurrogateQueryRegistry.instance().getSurrogateQuery(supplierKey); + if (surrogateQuery == null) { + throw new IllegalStateException( + String.format("Surrogate query for feature %s not found", + supplierKey.getPrettyPrintableName())); + } + addTrace(typeConstraint, new PositivePatternCall(getCopiedBody(), variablesTuple, surrogateQuery)); + } else { + addTrace(typeConstraint, new TypeConstraint(getCopiedBody(), variablesTuple, supplierKey)); + } + } + }; + PBody modifiedBody = copier.getCopiedBody(); + replacedBodies.add(modifiedBody); + modifiedBody.setStatus(PQueryStatus.OK); + } + return new PDisjunction(replacedBodies); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java new file mode 100644 index 00000000..10337979 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/VariableMappingExpressionEvaluatorWrapper.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Grill Balázs, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.psystem.rewriters; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; +import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; +import tools.refinery.viatra.runtime.matchers.psystem.PVariable; +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * A wrapper for {@link IExpressionEvaluator} which is capable of correctly mapping variable names used by the + * expression. + * + * @author Grill Balázs + * + */ +class VariableMappingExpressionEvaluatorWrapper implements IExpressionEvaluator { + + private final IExpressionEvaluator wrapped; + private final Map variableMapping; + + public VariableMappingExpressionEvaluatorWrapper(IExpressionEvaluator wrapped, + Map variableMapping) { + + // Support to rewrap an already wrapped expression. + boolean rewrap = wrapped instanceof VariableMappingExpressionEvaluatorWrapper; + this.wrapped = rewrap ? ((VariableMappingExpressionEvaluatorWrapper)wrapped).wrapped : wrapped; + + // Instead of just saving the reference of the mapping, save the actual (trimmed) state of the mapping as it + // may change during copying (especially during flattening). A LinkedHashMap is used to retain ordering of + // original parameter names iterator. + this.variableMapping = new LinkedHashMap<>(); + + // Index map by variable names + Map names = new HashMap<>(); + for (PVariable originalVar : variableMapping.keySet()) { + names.put(originalVar.getName(), originalVar); + } + + // In case of rewrapping, current names are contained by the previous mapping + Map previousMapping = null; + if (rewrap){ + previousMapping = ((VariableMappingExpressionEvaluatorWrapper)wrapped).variableMapping; + } + + // Populate mapping + for (String inputParameterName : this.wrapped.getInputParameterNames()) { + String parameterName = rewrap ? previousMapping.get(inputParameterName) : inputParameterName; + Preconditions.checkArgument(parameterName != null); + PVariable original = names.get(parameterName); + Preconditions.checkArgument(original != null); + PVariable mapped = variableMapping.get(original); + if (mapped != null){ + this.variableMapping.put(inputParameterName, mapped.getName()); + } + } + } + + @Override + public String getShortDescription() { + return wrapped.getShortDescription(); + } + + @Override + public Iterable getInputParameterNames() { + return variableMapping.values(); + } + + @Override + public Object evaluateExpression(final IValueProvider provider) throws Exception { + return wrapped.evaluateExpression(variableName -> { + String mappedVariableName = variableMapping.get(variableName); + Preconditions.checkArgument(mappedVariableName != null, "Could not find variable %s", variableName); + return provider.getValue(mappedVariableName); + }); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java new file mode 100644 index 00000000..a26d9193 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/AbstractTuple.java @@ -0,0 +1,136 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +/** + * Common implementation methods for immutable and volatile tuples. The class should not be used directly in client + * code, except for the definition of new tuple implementations. + * + * @author Zoltan Ujhelyi + * @since 1.7 + */ +public abstract class AbstractTuple implements ITuple { + + /** + * As the tuple is supposed to be immutable, do not modify the returned array. + * + * @return the array containing all elements of this Tuple + */ + @Override + public Object[] getElements() { + Object[] allElements = new Object[getSize()]; + for (int i = 0; i < allElements.length; ++i) + allElements[i] = get(i); + return allElements; + } + + /** + * @return the set containing all distinct elements of this Tuple, cast as type T + */ + @Override + @SuppressWarnings("unchecked") + public Set getDistinctElements() { + Set result = new HashSet(); + Object[] elements = getElements(); + for (Object object : elements) { + result.add((T) object); + } + return result; + } + + /** + * Calculates an inverted index of the elements of this pattern. For each element, the index of the (last) + * occurrence is calculated. + * + * @return the inverted index mapping each element of this pattern to its index in the array + */ + @Override + public Map invertIndex() { + Map result = new HashMap(); + for (int i = 0; i < getSize(); i++) + result.put(get(i), i); + return result; + } + + /** + * Calculates an inverted index of the elements of this pattern. For each element, the index of all of its + * occurrences is calculated. + * + * @return the inverted index mapping each element of this pattern to its index in the array + */ + @Override + public Map> invertIndexWithMupliplicity() { + Map> result = new HashMap>(); + for (int i = 0; i < getSize(); i++) { + Object value = get(i); + List indices = result.computeIfAbsent(value, v -> new ArrayList<>()); + indices.add(i); + } + return result; + } + + /** + * @since 1.7 + */ + protected IndexOutOfBoundsException raiseIndexingError(int index) { + return new IndexOutOfBoundsException( + String.format("No value at position %d for %s instance %s", index, getClass().getSimpleName(), this)); + } + + /** + * Compares the elements stored in this tuple to another tuple + */ + protected boolean internalEquals(ITuple other) { + if (getSize() != other.getSize()) + return false; + boolean result = true; + for (int i = 0; result && i < getSize(); ++i) { + Object ours = get(i); + Object theirs = other.get(i); + result = result && Objects.equals(ours, theirs); + } + return result; + } + + @Override + public String toString() { + StringBuilder s = new StringBuilder(); + s.append("T("); + for (Object o : getElements()) { + s.append(o == null ? "null" : o.toString()); + s.append(';'); + } + s.append(')'); + return s.toString(); + } + + /** + * @since 1.7 + */ + protected int doCalcHash() { + final int PRIME = 31; + int hash = 1; + for (int i = 0; i < getSize(); i++) { + hash = PRIME * hash; + Object element = get(i); + if (element != null) + hash += element.hashCode(); + } + return hash; + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java new file mode 100644 index 00000000..6f46b763 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseFlatTuple.java @@ -0,0 +1,20 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +/** + * Base class for all flat tuple implementations. + * Flat tuples store all elements locally (do not reference other tuples). + * + * @author Gabor Bergmann + * @since 1.7 + */ +public abstract class BaseFlatTuple extends Tuple { + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java new file mode 100644 index 00000000..03f9ea89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/BaseLeftInheritanceTuple.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +/** + * Common functionality of left inheritance tuple implementations. + * + *

Left inheritance tuples inherit their first few elements from another tuple, + * and extend it with additional "local" elements. + * + * @author Gabor Bergmann + * @since 1.7 + */ +public abstract class BaseLeftInheritanceTuple extends Tuple { + + /** + * The number of elements that aren't stored locally, but inherited from an ancestor Tuple instead. + */ + protected final int inheritedIndex; + /** + * This object contains the same elements as the ancestor on the first inheritedIndex positions + */ + protected final Tuple ancestor; + + /** + * @param ancestor + */ + public BaseLeftInheritanceTuple(Tuple ancestor) { + super(); + this.ancestor = ancestor; + this.inheritedIndex = ancestor.getSize(); + } + + /** + * @return the number of local (non-inherited) elements + */ + public abstract int getLocalSize(); + + /** + * Optimized equals calculation (prediction: true, since hash values match) + */ + @Override + protected boolean internalEquals(ITuple other) { + if (other instanceof BaseLeftInheritanceTuple) { + BaseLeftInheritanceTuple blit = (BaseLeftInheritanceTuple) other; + if (blit.inheritedIndex == this.inheritedIndex) { + if (this.ancestor.equals(blit.ancestor)) { + return localEquals(blit); + } else return false; + } + } + return super.internalEquals(other); + } + + /** + * Checks the equivalence of local elements only, after ancestor tuple has been determined to be equal. + */ + protected abstract boolean localEquals(BaseLeftInheritanceTuple other); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java new file mode 100644 index 00000000..8bbb0ac2 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.Arrays; + +/** + * Default Tuple implementation, with statically unknown arity. + * @author Gabor Bergmann + */ +public final class FlatTuple extends BaseFlatTuple { + + /** + * Array of substituted values. DO NOT MODIFY! Use Constructor to build a new instance instead. + */ + private final Object[] elements; + + /** + * Creates a FlatTuple instance, fills it with the given array. + *

Users should consider calling {@link Tuples#flatTupleOf(Object...)} instead to save memory on low-arity tuples. + * + * @param elements + * array of substitution values + */ + protected FlatTuple(Object... elements) { + this.elements = Arrays.copyOf(elements, elements.length); + calcHash(); + } + + @Override + public Object get(int index) { + return elements[index]; + } + + @Override + public int getSize() { + return elements.length; + } + + @Override + public Object[] getElements() { + return elements; + } + + @Override + protected boolean internalEquals(ITuple other) { + if (other instanceof FlatTuple) { + return Arrays.equals(elements, ((FlatTuple) other).elements); + } else + return super.internalEquals(other); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java new file mode 100644 index 00000000..93f412b7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple0.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +/** + * Flat tuple with statically known arity of 0. + * + * @author Gabor Bergmann + * @since 1.7 + * + */ +public final class FlatTuple0 extends BaseFlatTuple { + protected static final FlatTuple0 INSTANCE = new FlatTuple0(); + + private FlatTuple0() { + calcHash(); + } + + @Override + public int getSize() { + return 0; + } + + @Override + public Object get(int index) { + throw raiseIndexingError(index); + } + + private static final Object[] NULLARY_ARRAY = new Object[0]; + + @Override + public Object[] getElements() { + return NULLARY_ARRAY; + } + + @Override + protected boolean internalEquals(ITuple other) { + return 0 == other.getSize(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java new file mode 100644 index 00000000..b3b1c312 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple1.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.Objects; + +/** + * Flat tuple with statically known arity of 1. + * + * @author Gabor Bergmann + * @since 1.7 + * + */ +public final class FlatTuple1 extends BaseFlatTuple { + private final Object element0; + + protected FlatTuple1(Object element0) { + this.element0 = element0; + calcHash(); + } + + @Override + public int getSize() { + return 1; + } + + @Override + public Object get(int index) { + if (index == 0) return element0; + else throw raiseIndexingError(index); + } + + @Override + protected boolean internalEquals(ITuple other) { + return 1 == other.getSize() && + Objects.equals(element0, other.get(0)); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java new file mode 100644 index 00000000..2dcfd718 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple2.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.Objects; + +/** + * Flat tuple with statically known arity of 2. + * + * @author Gabor Bergmann + * @since 1.7 + * + */ +public final class FlatTuple2 extends BaseFlatTuple { + private final Object element0; + private final Object element1; + + protected FlatTuple2(Object element0, Object element1) { + this.element0 = element0; + this.element1 = element1; + calcHash(); + } + + @Override + public int getSize() { + return 2; + } + + @Override + public Object get(int index) { + switch(index) { + case 0 : return element0; + case 1 : return element1; + default: throw raiseIndexingError(index); + } + } + + @Override + protected boolean internalEquals(ITuple other) { + return 2 == other.getSize() && + Objects.equals(element0, other.get(0)) && + Objects.equals(element1, other.get(1)); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java new file mode 100644 index 00000000..50cee57e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple3.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.Objects; + +/** + * Flat tuple with statically known arity of 3. + * + * @author Gabor Bergmann + * @since 1.7 + * + */ +public final class FlatTuple3 extends BaseFlatTuple { + private final Object element0; + private final Object element1; + private final Object element2; + + protected FlatTuple3(Object element0, Object element1, Object element2) { + this.element0 = element0; + this.element1 = element1; + this.element2 = element2; + calcHash(); + } + + @Override + public int getSize() { + return 3; + } + + @Override + public Object get(int index) { + switch (index) { + case 0: return element0; + case 1: return element1; + case 2: return element2; + default: throw raiseIndexingError(index); + } + } + + @Override + protected boolean internalEquals(ITuple other) { + return 3 == other.getSize() && + Objects.equals(element0, other.get(0)) && + Objects.equals(element1, other.get(1)) && + Objects.equals(element2, other.get(2)); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java new file mode 100644 index 00000000..024c94f4 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/FlatTuple4.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.Objects; + +/** + * Flat tuple with statically known arity of 4. + * + * @author Gabor Bergmann + * @since 1.7 + * + */ +public final class FlatTuple4 extends BaseFlatTuple { + private final Object element0; + private final Object element1; + private final Object element2; + private final Object element3; + + protected FlatTuple4(Object element0, Object element1, Object element2, Object element3) { + this.element0 = element0; + this.element1 = element1; + this.element2 = element2; + this.element3 = element3; + calcHash(); + } + + @Override + public int getSize() { + return 4; + } + + @Override + public Object get(int index) { + switch(index) { + case 0: return element0; + case 1: return element1; + case 2: return element2; + case 3: return element3; + default: throw raiseIndexingError(index); + } + } + + @Override + protected boolean internalEquals(ITuple other) { + return 4 == other.getSize() && + Objects.equals(element0, other.get(0)) && + Objects.equals(element1, other.get(1)) && + Objects.equals(element2, other.get(2)) && + Objects.equals(element3, other.get(3)); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java new file mode 100644 index 00000000..f5dab2a5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/IModifiableTuple.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +/** + * A tuple that allows modifying the underlying value. Should not be used for non-volatile tuples. + * + * @author Zoltan Ujhelyi + * @since 1.7 + */ +public interface IModifiableTuple extends ITuple { + + /** + * Sets the selected value for a tuple + * + * @pre: 0 <= index < getSize() + * + */ + void set(int index, Object value); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java new file mode 100644 index 00000000..92014781 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/ITuple.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Represents both mutable and immutable tuples + * + * @author Zoltan Ujhelyi + * @since 1.7 + * + */ +public interface ITuple { + + /** + * @pre: 0 <= index < getSize() + * + * @return the element at the specified index + */ + Object get(int index); + + /** + * As the tuple is supposed to be immutable, do not modify the returned array. + * @return the array containing all elements of this Tuple + */ + Object[] getElements(); + + /** + * @return the set containing all distinct elements of this Tuple, cast as type T + */ + Set getDistinctElements(); + + /** + * @return number of elements + */ + int getSize(); + + /** + * Calculates an inverted index of the elements of this pattern. For each element, the index of the (last) + * occurrence is calculated. + * + * @return the inverted index mapping each element of this pattern to its index in the array + */ + Map invertIndex(); + + /** + * Calculates an inverted index of the elements of this pattern. For each element, the index of all of its + * occurrences is calculated. + * + * @return the inverted index mapping each element of this pattern to its index in the array + */ + Map> invertIndexWithMupliplicity(); + + Tuple toImmutable(); +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java new file mode 100644 index 00000000..dcdfc376 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/LeftInheritanceTuple.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.Arrays; +import java.util.Objects; + +/** + * + * Tuple that inherits another tuple on the left. + * + * @author Gabor Bergmann + * + */ +public final class LeftInheritanceTuple extends BaseLeftInheritanceTuple { + /** + * Array of substituted values above inheritedIndex. DO NOT MODIFY! Use Constructor to build a new instance instead. + */ + private final Object[] localElements; + + // + // /** + // * Creates a Tuple instance, fills it with the given array. + // * @pre: no elements are null + // * @param elements array of substitution values + // */ + // public Tuple(Object[] elements) + // { + // this.localElements = elements; + // this.ancestor=null; + // this.inheritedIndex = 0; + // calcHash(); + // } + + + + /** + * Creates a Tuple instance, lets it inherit from an ancestor, extends it with a given array. @pre: no elements are + * null + * + * @param localElements + * array of substitution values + */ + LeftInheritanceTuple(Tuple ancestor, Object[] localElements) { + super(ancestor); + this.localElements = localElements; + calcHash(); + } + + // + // /** + // * Creates a Tuple instance of size one, fills it with the given object. + // * @pre: o!=null + // * @param o the single substitution + // */ + // public Tuple(Object o) + // { + // localElements = new Object [1]; + // localElements[0] = o; + // this.ancestor=null; + // this.inheritedIndex = 0; + // calcHash(); + // } + // + // /** + // * Creates a Tuple instance of size two, fills it with the given objects. + // * @pre: o1!=null, o2!=null + // */ + // public Tuple(Object o1, Object o2) + // { + // localElements = new Object [2]; + // localElements[0] = o1; + // localElements[1] = o2; + // this.ancestor=null; + // this.inheritedIndex = 0; + // calcHash(); + // } + // + // /** + // * Creates a Tuple instance of size three, fills it with the given + // objects. + // * @pre: o1!=null, o2!=null, o3!=null + // */ + // public Tuple(Object o1, Object o2, Object o3) + // { + // localElements = new Object [3]; + // localElements[0] = o1; + // localElements[1] = o2; + // localElements[2] = o3; + // this.ancestor=null; + // this.inheritedIndex = 0; + // calcHash(); + // } + + /** + * @return number of elements + */ + public int getSize() { + return inheritedIndex + localElements.length; + } + + @Override + public int getLocalSize() { + return localElements.length; + } + + /** + * @pre: 0 <= index < getSize() + * + * @return the element at the specified index + */ + public Object get(int index) { + return (index < inheritedIndex) ? ancestor.get(index) : localElements[index - inheritedIndex]; + } + + /** + * Optimized hash calculation + */ + @Override + void calcHash() { + final int PRIME = 31; + cachedHash = ancestor.hashCode(); + for (int i = 0; i < localElements.length; i++) { + cachedHash = PRIME * cachedHash; + Object element = localElements[i]; + if (element != null) + cachedHash += element.hashCode(); + } + } + + /** + * Optimized equals calculation (prediction: true, since hash values match) + */ + @Override + protected boolean localEquals(BaseLeftInheritanceTuple other) { + if (other instanceof LeftInheritanceTuple) { + LeftInheritanceTuple lit = (LeftInheritanceTuple)other; + return Arrays.equals(this.localElements, lit.localElements); + } else { + if (localElements.length != other.getLocalSize()) + return false; + int index = inheritedIndex; + for (int i = 0; i indicesSorted and isNonrepeating may be OPTIONALLY given if known. + * @since 2.0 + */ + protected TupleMask(int[] indices, int sourceWidth, int[] indicesSorted, Boolean isNonrepeating) { + this.indices = indices; + this.sourceWidth = sourceWidth; + this.indicesSorted = indicesSorted; + this.isNonrepeating = isNonrepeating; + } + + /** + * Creates a TupleMask instance that selects given positions. + * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards. + * + *

indicesSorted and isNonrepeating may be OPTIONALLY given if known. + * @since 2.0 + */ + protected static TupleMask fromSelectedIndicesInternal( + int[] selectedIndices, int sourceArity, + int[] indicesSorted, Boolean isNonrepeating) + { + if (selectedIndices.length == 0) // is it nullary? + return new TupleMask0(sourceArity); + + // is it identity? + boolean identity = sourceArity == selectedIndices.length; + if (identity) { + for (int k=0; k < sourceArity; ++k) { + if (selectedIndices[k] != k) { + identity = false; + break; + } + } + } + if (identity) + return new TupleMaskIdentity(selectedIndices, sourceArity); + + // generic case + return new TupleMask(selectedIndices, sourceArity, indicesSorted, isNonrepeating); + } + + /** + * Creates a TupleMask instance that selects given positions in monotonically increasing order. + * The mask takes ownership of the array selectedIndices, the client must not modify it afterwards. + * @since 2.0 + */ + protected static TupleMask fromSelectedMonotonicIndicesInternal(int[] selectedIndices, int sourceArity) + { + return fromSelectedIndicesInternal(selectedIndices, sourceArity, selectedIndices /* also sorted */, true); + } + + /** + * Creates a TupleMask instance of the given size that maps the first 'size' elements intact + */ + public static TupleMask linear(int size, int sourceWidth) { + if (size == sourceWidth) return new TupleMaskIdentity(sourceWidth); + int[] indices = constructLinearSequence(size); + return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); + } + + /** + * An array containing the first {@link size} nonnegative integers in order + * @since 2.0 + */ + protected static int[] constructLinearSequence(int size) { + int[] indices = new int[size]; + for (int i = 0; i < size; i++) + indices[i] = i; + return indices; + } + + /** + * Creates a TupleMask instance of the given size that maps every single element intact + */ + public static TupleMask identity(int size) { + return new TupleMaskIdentity(size); + } + + /** + * Creates a TupleMask instance of the given size that does not emit output. + */ + public static TupleMask empty(int sourceWidth) { + return linear(0, sourceWidth); + } + + /** + * Creates a TupleMask instance that maps the tuple intact save for a single element at the specified index which is + * omitted + */ + public static TupleMask omit(int omission, int sourceWidth) { + int size = sourceWidth - 1; + int[] indices = new int[size]; + for (int i = 0; i < omission; i++) + indices[i] = i; + for (int i = omission; i < size; i++) + indices[i] = i + 1; + return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); + } + + + /** + * Creates a TupleMask instance that selects positions where keep is true + * @since 1.7 + */ + public static TupleMask fromKeepIndicators(boolean[] keep) { + int size = 0; + for (int k = 0; k < keep.length; ++k) + if (keep[k]) + size++; + if (size == keep.length) return new TupleMaskIdentity(size); + int[] indices = new int[size]; + int l = 0; + for (int k = 0; k < keep.length; ++k) + if (keep[k]) + indices[l++] = k; + return fromSelectedMonotonicIndicesInternal(indices, keep.length); + } + + /** + * Creates a TupleMask instance that selects given positions. + * @since 1.7 + */ + public static TupleMask fromSelectedIndices(int sourceArity, Collection selectedIndices) { + int[] selected = integersToIntArray(selectedIndices); + return fromSelectedIndicesInternal(selected, sourceArity, null, null); + } + /** + * Creates a TupleMask instance that selects given positions. + * @since 1.7 + */ + public static TupleMask fromSelectedIndices(int sourceArity, int[] selectedIndices) { + return fromSelectedIndicesInternal(Arrays.copyOf(selectedIndices, selectedIndices.length), sourceArity, null, null); + } + /** + * Creates a TupleMask instance that selects non-null positions of a given tuple + * @since 1.7 + */ + public static TupleMask fromNonNullIndices(ITuple tuple) { + List indices = new ArrayList<>(); + for (int i=0; i < tuple.getSize(); i++) { + if (tuple.get(i) != null) { + indices.add(i); + } + } + if (indices.size() == tuple.getSize()) return new TupleMaskIdentity(indices.size()); + return fromSelectedMonotonicIndicesInternal(integersToIntArray(indices), tuple.getSize()); + } + /** + * @since 1.7 + */ + public static int[] integersToIntArray(Collection selectedIndices) { + int[] selected = new int[selectedIndices.size()]; + int k=0; + for (Integer integer : selectedIndices) { + selected[k++] = integer; + } + return selected; + } + + + /** + * Creates a TupleMask instance that moves an element from one index to other, shifting the others if neccessary. + */ + public static TupleMask displace(int from, int to, int sourceWidth) { + if (from == to) return new TupleMaskIdentity(sourceWidth); + int[] indices = new int[sourceWidth]; + for (int i = 0; i < sourceWidth; i++) + if (i == to) + indices[i] = from; + else if (i >= from && i < to) + indices[i] = i + 1; + else if (i > to && i <= from) + indices[i] = i - 1; + else + indices[i] = i; + return fromSelectedIndicesInternal(indices, sourceWidth, null, true); + } + + /** + * Creates a TupleMask instance that selects a single element of the tuple. + */ + public static TupleMask selectSingle(int selected, int sourceWidth) { + int[] indices = { selected }; + return fromSelectedMonotonicIndicesInternal(indices, sourceWidth); + } + + /** + * Creates a TupleMask instance that selects whatever is selected by left, and appends whatever is selected by + * right. PRE: left and right have the same sourcewidth + */ + public static TupleMask append(TupleMask left, TupleMask right) { + int leftLength = left.indices.length; + int rightLength = right.indices.length; + int[] indices = new int[leftLength + rightLength]; + for (int i = 0; i < leftLength; ++i) + indices[i] = left.indices[i]; + for (int i = 0; i < rightLength; ++i) + indices[i + leftLength] = right.indices[i]; + return fromSelectedIndicesInternal(indices, left.sourceWidth, null, null); + } + + /** + * Generates indicesSorted from indices on demand + */ + void ensureIndicesSorted() { + if (indicesSorted == null) { + indicesSorted = new int[indices.length]; + List list = new LinkedList(); + for (int i = 0; i < indices.length; ++i) + list.add(indices[i]); + java.util.Collections.sort(list); + int i = 0; + for (Integer a : list) + indicesSorted[i++] = a; + } + } + + + + /** + * Returns the first index of the source that is not selected by the mask, or empty if all indices are selected. + *

PRE: mask indices are all different + * @since 2.0 + */ + public OptionalInt getFirstOmittedIndex() { + ensureIndicesSorted(); + int column = 0; + while (column < getSize() && indicesSorted[column] == column) column++; + if (column < getSourceWidth()) return OptionalInt.of(column); + else return OptionalInt.empty(); + } + + + /** + * Returns a selected masked value from the selected tuple. + * @pre: 0 <= index < getSize() + * @since 1.7 + */ + public Object getValue(ITuple original, int index) { + return original.get(indices[index]); + } + + /** + * Sets the selected value in the original tuple based on the mask definition + * + * @pre: 0 <= index < getSize() + * @since 1.7 + */ + public void set(IModifiableTuple tuple, int index, Object value) { + tuple.set(indices[index], value); + } + + /** + * Generates an immutable, masked view of the original tuple. + *

The new tuple will have arity {@link #getSize()}, + * and will consist of the elements of the original tuple, at positions indicated by this mask. + * @since 1.7 + */ + public Tuple transform(ITuple original) { + switch (indices.length) { + case 0: + return FlatTuple0.INSTANCE; + case 1: + return new FlatTuple1(original.get(indices[0])); + case 2: + return new FlatTuple2(original.get(indices[0]), original.get(indices[1])); + case 3: + return new FlatTuple3(original.get(indices[0]), original.get(indices[1]), original.get(indices[2])); + case 4: + return new FlatTuple4(original.get(indices[0]), original.get(indices[1]), original.get(indices[2]), original.get(indices[3])); + default: + Object signature[] = new Object[indices.length]; + for (int i = 0; i < indices.length; ++i) + signature[i] = original.get(indices[i]); + return new FlatTuple(signature); + } + } + + /** + * @return true iff no two selected indices are the same + * @since 2.0 + */ + public boolean isNonrepeating() { + if (isNonrepeating == null) { + ensureIndicesSorted(); + int previous = -1; + int i; + for (i = 0; i < sourceWidth && previous != indicesSorted[i]; ++i) { + previous = indicesSorted[i]; + } + isNonrepeating = (i == sourceWidth); // if not, stopped due to detected repetition + } + return isNonrepeating; + } + + /** + * Returns a tuple `result` that satisfies `this.transform(result).equals(masked)`. Positions of the result tuple + * that are not determined this way will be filled with null. + * + * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true + * @since 1.7 + */ + public Tuple revertFrom(ITuple masked) { + Object[] signature = new Object[sourceWidth]; + for (int i = 0; i < indices.length; ++i) + signature[indices[i]] = masked.get(i); + return Tuples.flatTupleOf(signature); + } + + /** + * Returns a tuple `result`, same arity as the original tuple, that satisfies + * `this.transform(result).equals(this.transform(tuple))`. + * Positions of the result tuple that are not determined this way will be filled with null. + *

In other words, a copy of the original tuple is returned, + * with null substituted at each position that is not selected by this mask. + * + * @pre: all indices of the mask must be different, i.e {@link #isNonrepeating()} must return true + * @since 2.1 + */ + public Tuple keepSelectedIndices(ITuple original) { + Object[] signature = new Object[sourceWidth]; + for (int i = 0; i < indices.length; ++i) + signature[indices[i]] = original.get(indices[i]); + return Tuples.flatTupleOf(signature); + } + + /** + * Generates an immutable, masked view of the original tuple. + *

The list will have arity {@link #getSize()}, + * and will consist of the elements of the original tuple, at positions indicated by this mask. + */ + public List transform(List original) { + List signature = new ArrayList(indices.length); + for (int i = 0; i < indices.length; ++i) + signature.add(original.get(indices[i])); + return signature; + } + + /** + * Transforms a given mask directly, instead of transforming tuples that were transformed by the other mask. + * + * @return a mask that cascades the effects this mask after the mask provided as parameter. + */ + public TupleMask transform(TupleMask mask) { + int[] cascadeIndices = new int[indices.length]; + for (int i = 0; i < indices.length; ++i) + cascadeIndices[i] = mask.indices[indices[i]]; + return fromSelectedIndicesInternal(cascadeIndices, mask.sourceWidth, null, null); + } + + // /** + // * Generates a complementer mask that maps those elements that were + // untouched by the original mask. + // * Ordering is left intact. + // * A Tuple is used for reference concerning possible equalities among + // elements. + // */ + // public TupleMask complementer(Tuple reference) + // { + // HashSet touched = new HashSet(); + // LinkedList untouched = new LinkedList(); + // + // for (int index : indices) touched.add(reference.get(index)); + // for (int index=0; index"); + for (int i : indices) { + s.append(i); + s.append(','); + } + s.append(')'); + return s.toString(); + } + + /** + * Returns the size of the masked tuples described by this mask + * @since 1.7 + */ + public int getSize() { + return indices.length; + } + + /** + * Returns the size of the original tuples handled by this mask + * @since 1.7 + */ + public int getSourceWidth() { + return sourceWidth; + } + + + /** + * @return true iff this mask is a no-op + * @since 2.0 + */ + public boolean isIdentity() { + // Contract: if identity mask, a specialized subclass is constructed instead + return false; + } + + /** + * Transforms the given list by applying the mask and putting all results into a set; keeping only a single element + * in case of the mapping result in duplicate values. + * + * @since 1.7 + */ + public Set transformUnique(List original) { + Set signature = new HashSet<>(); + for (int i = 0; i < indices.length; ++i) + signature.add(original.get(indices[i])); + return signature; + } + + /** + * @return the list of selected indices + * @since 2.1 + */ + public List getIndicesAsList() { + List result = new ArrayList(indices.length); + for (int i = 0; i < indices.length; ++i) + result.add(indices[i]); + return result; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java new file mode 100644 index 00000000..5a0c79ff --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMask0.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.Collections; +import java.util.List; + +/** + * @author Gabor Bergmann + * @since 1.7 + */ +public final class TupleMask0 extends TupleMask { + + private final static int[] EMPTY_ARRAY = {}; + + /** + * PRE: indices.length == 0 + */ + TupleMask0(int sourceWidth) { + super(EMPTY_ARRAY, sourceWidth, EMPTY_ARRAY, true); + } + + @Override + public List transform(List original) { + return Collections.emptyList(); + } + + @Override + public Tuple transform(ITuple original) { + return Tuples.staticArityFlatTupleOf(); + } + + @Override + public TupleMask transform(TupleMask mask) { + return new TupleMask0(mask.sourceWidth); + } + + @Override + public Tuple combine(Tuple unmasked, Tuple masked, boolean useInheritance, boolean asComplementer) { + if (asComplementer) + return unmasked; + else + return super.combine(unmasked, masked, useInheritance, asComplementer); + } + + @Override + public boolean isIdentity() { + return 0 == sourceWidth; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java new file mode 100644 index 00000000..62746587 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleMaskIdentity.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.List; + +/** + * @author Gabor Bergmann + * @since 1.7 + */ +public final class TupleMaskIdentity extends TupleMask { + + TupleMaskIdentity(int sourceWidth) { + this(constructLinearSequence(sourceWidth), sourceWidth); + } + TupleMaskIdentity(int[] indices, int sourceWidth) { + super(indices, sourceWidth, indices, true); + } + + @Override + public List transform(List original) { + return original; + } + + @Override + public Tuple transform(ITuple original) { + return original.toImmutable(); + } + + @Override + public TupleMask transform(TupleMask mask) { + return mask; + } + + @Override + public Tuple revertFrom(ITuple masked) { + return masked.toImmutable(); + } + + @Override + public boolean isIdentity() { + return true; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java new file mode 100644 index 00000000..79193516 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/TupleValueProvider.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.psystem.IValueProvider; + +/** + * @author Zoltan Ujhelyi + * @since 1.7 + */ +public class TupleValueProvider implements IValueProvider { + + final ITuple tuple; + final Map indexMapping; + + /** + * Wraps a tuple with an index mapping + * @param tuple + * @param indexMapping + */ + public TupleValueProvider(ITuple tuple, Map indexMapping) { + super(); + this.tuple = tuple; + this.indexMapping = indexMapping; + } + + @Override + public Object getValue(String variableName) { + Integer index = indexMapping.get(variableName); + if (index == null) { + throw new IllegalArgumentException(String.format("Variable %s is not present in mapping.", variableName)); + } + Object value = tuple.get(index); + if (value == null) { + throw new IllegalArgumentException(String.format("Variable %s is not found using index %d.", variableName, index)); + } + return value; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java new file mode 100644 index 00000000..5e41d7d8 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/Tuples.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +/** + * Common static factory utilities for tuples. + * + * @author Gabor Bergmann + * @since 1.7 + */ +public class Tuples { + + private Tuples() { + // Empty utility class constructor + } + + /** + * Creates a flat tuple consisting of the given elements. + * For low-arity tuples, specialized implementations + * (such as {@link FlatTuple2}) will be instantiated. + * + *

In case the exact arity is statically known, + * it may be more efficient for the client to instantiate + * the appropriate specialized implementation + * (via {@link #staticArityFlatTupleOf(Object, Object)} etc. + * or {@link #wideFlatTupleOf(Object...)}), + * instead of invoking this method. + * This method does a runtime arity check, and therefore + * also appropriate if the arity is determined at runtime. + */ + public static Tuple flatTupleOf(Object... elements) { + switch (elements.length) { + case 0: + return FlatTuple0.INSTANCE; + case 1: + return new FlatTuple1(elements[0]); + case 2: + return new FlatTuple2(elements[0], elements[1]); + case 3: + return new FlatTuple3(elements[0], elements[1], elements[2]); + case 4: + return new FlatTuple4(elements[0], elements[1], elements[2], elements[3]); + default: + return new FlatTuple(elements); + } + } + /** + * Creates a left inheritance tuple that extends an ancestor tuple + * by the given "local" elements. + * For locally low-arity tuples, specialized implementations + * (such as {@link LeftInheritanceTuple2}) will be instantiated. + * + *

In case the exact arity is statically known, + * it may be more efficient for the client to instantiate + * the appropriate specialized implementation + * (via {@link #staticArityLeftInheritanceTupleOf(Object, Object)} etc. + * or {@link #wideLeftInheritanceTupleOf(Object...)}), + * instead of invoking this method. + * This method does a runtime arity check, and therefore + * also appropriate if the arity is determined at runtime. + */ + public static Tuple leftInheritanceTupleOf(Tuple ancestor, Object... localElements) { + switch (localElements.length) { + case 0: + return ancestor; + case 1: + return new LeftInheritanceTuple1(ancestor, localElements[0]); + case 2: + return new LeftInheritanceTuple2(ancestor, localElements[0], localElements[1]); + case 3: + return new LeftInheritanceTuple3(ancestor, localElements[0], localElements[1], localElements[2]); + case 4: + return new LeftInheritanceTuple4(ancestor, localElements[0], localElements[1], localElements[2], localElements[3]); + default: + return new LeftInheritanceTuple(ancestor, localElements); + } + } + + /** + * Creates a flat tuple consisting of no elements. + */ + public static Tuple staticArityFlatTupleOf() { + return FlatTuple0.INSTANCE; + } + /** + * Creates a flat tuple consisting of the given single element. + */ + public static Tuple staticArityFlatTupleOf(Object element) { + return new FlatTuple1(element); + } + /** + * Creates a flat tuple consisting of the given elements. + */ + public static Tuple staticArityFlatTupleOf(Object element0, Object element1) { + return new FlatTuple2(element0, element1); + } + /** + * Creates a flat tuple consisting of the given elements. + */ + public static Tuple staticArityFlatTupleOf(Object element0, Object element1, Object element2) { + return new FlatTuple3(element0, element1, element2); + } + /** + * Creates a flat tuple consisting of the given elements. + */ + public static Tuple staticArityFlatTupleOf(Object element0, Object element1, Object element2, Object element3) { + return new FlatTuple4(element0, element1, element2, element3); + } + /** + * Creates a flat tuple consisting of the given elements. + *

Invoke this only if it is statically known that the tuple will be wide. + * Otherwise, use {@link #flatTupleOf(Object...)}. + */ + public static Tuple wideFlatTupleOf(Object... elements) { + return new FlatTuple(elements); + } + + /** + * Creates a left inheritance tuple consisting of the given single local element. + */ + public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element) { + return new LeftInheritanceTuple1(ancestor, element); + } + /** + * Creates a left inheritance tuple consisting of the given local elements. + */ + public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1) { + return new LeftInheritanceTuple2(ancestor, element0, element1); + } + /** + * Creates a left inheritance tuple consisting of the given local elements. + */ + public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1, Object element2) { + return new LeftInheritanceTuple3(ancestor, element0, element1, element2); + } + /** + * Creates a left inheritance tuple consisting of the given local elements. + */ + public static Tuple staticArityLeftInheritanceTupleOf(Tuple ancestor, Object element0, Object element1, Object element2, Object element3) { + return new LeftInheritanceTuple4(ancestor, element0, element1, element2, element3); + } + /** + * Creates a left inheritance tuple consisting of the given local elements. + *

Invoke this only if it is statically known that the tuple will be wide. + * Otherwise, use {@link #leftInheritanceTupleOf(Tuple, Object...)}. + */ + public static Tuple wideLeftInheritanceTupleOf(Tuple ancestor, Object... elements) { + return new LeftInheritanceTuple(ancestor, elements); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java new file mode 100644 index 00000000..f683d544 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileMaskedTuple.java @@ -0,0 +1,50 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * This class provides a volatile tuple view with a given mask of a given tuple instance. If the masked tuple changes, + * the view updates as well. + * + * @author Zoltan Ujhelyi + * @since 1.7 + * + */ +public class VolatileMaskedTuple extends VolatileTuple { + + protected final TupleMask mask; + protected ITuple source; + + public VolatileMaskedTuple(ITuple source, TupleMask mask) { + this.source = source; + this.mask = mask; + } + + public VolatileMaskedTuple(TupleMask mask) { + this(null, mask); + } + + public void updateTuple(ITuple newSource) { + source = newSource; + } + + @Override + public Object get(int index) { + Preconditions.checkState(source != null, "Source tuple is not set."); + return mask.getValue(source, index); + } + + @Override + public int getSize() { + return mask.getSize(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java new file mode 100644 index 00000000..92306c6e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileModifiableMaskedTuple.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.tuple; + +import tools.refinery.viatra.runtime.matchers.util.Preconditions; + +/** + * A masked tuple implementation that allows modifying the backing tuple. + * @author Zoltan Ujhelyi + * @since 1.7 + * + */ +public class VolatileModifiableMaskedTuple extends VolatileMaskedTuple implements IModifiableTuple { + + private IModifiableTuple modifiableTuple; + + public VolatileModifiableMaskedTuple(IModifiableTuple source, TupleMask mask) { + super(source, mask); + modifiableTuple = source; + } + + public VolatileModifiableMaskedTuple(TupleMask mask) { + this(null, mask); + } + + @Override + public void updateTuple(ITuple newSource) { + Preconditions.checkArgument(newSource instanceof IModifiableTuple, "Provided tuple does not support updates"); + this.updateTuple((IModifiableTuple)newSource); + } + + public void updateTuple(IModifiableTuple newSource) { + super.updateTuple(newSource); + modifiableTuple = newSource; + } + + @Override + public void set(int index, Object value) { + mask.set(modifiableTuple, index, value); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java new file mode 100644 index 00000000..699105a5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/tuple/VolatileTuple.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2010-2017 Zoltan Ujhelyi, IncQuery Labs + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.tuple; + +/** + * Mutable tuple without explicit modification commands. In practical terms, the values stored in a volatile tuple can + * be changed without any notification. + * + * @author Zoltan Ujhelyi + * @since 1.7 + * + */ +public abstract class VolatileTuple extends AbstractTuple { + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (!(obj instanceof ITuple)) + return false; + final ITuple other = (ITuple) obj; + return internalEquals(other); + } + + @Override + public int hashCode() { + return doCalcHash(); + } + + /** + * Creates an immutable tuple from the values stored in the tuple. The created tuple will not be updated when the + * current tuple changes. + */ + @Override + public Tuple toImmutable() { + return Tuples.flatTupleOf(getElements()); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java new file mode 100644 index 00000000..338990ab --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Accuracy.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +/** + * The degree of accuracy of a cardinality estimate + * @author Gabor Bergmann + * @since 2.1 + */ +public enum Accuracy { + EXACT_COUNT, + BEST_UPPER_BOUND, + BEST_LOWER_BOUND, + APPROXIMATION; + + /** + * Partial order comparison. + */ + public boolean atLeastAsPreciseAs(Accuracy other) { + switch (this) { + case EXACT_COUNT: return true; + case APPROXIMATION: return APPROXIMATION == other; + case BEST_UPPER_BOUND: return BEST_UPPER_BOUND == other || APPROXIMATION == other; + case BEST_LOWER_BOUND: return BEST_LOWER_BOUND == other || APPROXIMATION == other; + default: throw new IllegalArgumentException(); + } + } + + /** + * @return another accuracy value that is anti-monotonic to this one, + * i.e. an accuracy that should be used in the denominator to obtain a fraction with this accuracy + */ + public Accuracy reciprocal() { + switch(this) { + case APPROXIMATION: return APPROXIMATION; + case BEST_UPPER_BOUND: return BEST_LOWER_BOUND; + case BEST_LOWER_BOUND: return BEST_UPPER_BOUND; + case EXACT_COUNT: return EXACT_COUNT; + default: throw new IllegalArgumentException(); + } + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java new file mode 100644 index 00000000..1b09aec6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Clearable.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.util; + +/** + * @author Gabor Bergmann + * @since 1.7 + * An instance of clearable pattern memory. + */ +public interface Clearable { + /** + * Clear all partial matchings stored in memory + * + */ + void clear(); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java new file mode 100644 index 00000000..590a1ec3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/CollectionsFactory.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.function.Function; + +/** + * Factory class used as an accessor to Collections implementations. + * @author istvanrath + */ +public final class CollectionsFactory +{ + + /** + * Instantiates a new empty map. + * @since 1.7 + */ + public static Map createMap() { + return FRAMEWORK.createMap(); + } + + /** + * Instantiates a new map with the given initial contents. + * @since 1.7 + */ + public static Map createMap(Map initial) { + return FRAMEWORK.createMap(initial); + } + + /** + * Instantiates a new tree map. + * @since 2.3 + */ + public static TreeMap createTreeMap() { + return FRAMEWORK.createTreeMap(); + } + + /** + * Instantiates a new empty set. + * @since 1.7 + */ + public static Set createSet() { + return FRAMEWORK.createSet(); + } + + /** + * Instantiates a new set with the given initial contents. + * @since 1.7 + */ + public static Set createSet(Collection initial) { + return FRAMEWORK.createSet(initial); + } + + /** + * Instantiates an empty set; the key parameter is used to allow using this as a method reference as a + * {@link Function}, e.g. in {@link Map#computeIfAbsent(Object, Function)}. + * + * @param key + * the value of this parameter is ignored + * @since 2.0 + */ + public static Set emptySet(Object key) { + return FRAMEWORK.createSet(); + } + + /** + * Instantiates a new empty multiset. + * @since 1.7 + */ + public static IMultiset createMultiset() { + return FRAMEWORK.createMultiset(); + } + + /** + * Instantiates an empty multiset; the key parameter is used to allow using this as a method reference as a + * {@link Function}, e.g. in {@link Map#computeIfAbsent(Object, Function)}. + * + * @param key + * the value of this parameter is ignored + * @since 2.0 + */ + public static IMultiset emptyMultiset(Object key) { + return FRAMEWORK.createMultiset(); + } + + /** + * Instantiates a new empty delta bag. + * @since 1.7 + */ + public static IDeltaBag createDeltaBag() { + return FRAMEWORK.createDeltaBag(); + } + + /** + * Instantiates a new list that is optimized for registering observers / callbacks. + * @since 1.7 + */ + public static List createObserverList() { + return FRAMEWORK.createObserverList(); + } + + /** + * Instantiates a size-optimized multimap from keys to sets of values. + *

For a single key, many values can be associated according to the given bucket semantics. + *

The keys and values are stored as type fromKeys resp. ofValues; + * currently Object.class and Long.class are supported. + * @since 2.0 + */ + public static IMultiLookup createMultiLookup( + Class fromKeys, MemoryType toBuckets, Class ofValues) { + return FRAMEWORK.createMultiLookup(fromKeys, toBuckets, ofValues); + } + + /** + * Instantiates a memory storing values. + *

For a single key, many values can be associated according to the given memory semantics. + *

The values are stored as type 'values'; + * currently Object.class and Long.class are supported. + * @since 2.0 + */ + public static IMemory createMemory( + Class values, MemoryType memoryType) { + return FRAMEWORK.createMemory(values, memoryType); + } + + /** + * The type of {@link IMemory} + * @since 2.0 + * TODO add delta as type + */ + public enum MemoryType { + /** + * A single key-value pair is stored at most once + */ + SETS, + /** + * Duplicate key-value pairs allowed + */ + MULTISETS + } + + /** + * The collections framework of the current configuration. + * @since 1.7 + */ + private static final ICollectionsFramework FRAMEWORK = new EclipseCollectionsFactory(); + + /** + * Interface abstracting over a collections technology that provides custom collection implementations. + * @since 1.7 + */ + public static interface ICollectionsFramework { + + public abstract Map createMap(); + public abstract Map createMap(Map initial); + /** + * @since 2.3 + */ + public abstract TreeMap createTreeMap(); + public abstract Set createSet(); + public abstract Set createSet(Collection initial); + public abstract IMultiset createMultiset(); + public abstract IDeltaBag createDeltaBag(); + public abstract List createObserverList(); + + /** + * @since 2.0 + */ + public abstract IMultiLookup createMultiLookup( + Class fromKeys, MemoryType toBuckets, Class ofValues); + /** + * @since 2.0 + */ + public abstract IMemory createMemory(Class values, MemoryType memoryType); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java new file mode 100644 index 00000000..88f7ec00 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Direction.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +/** + * Indicates whether a propagated update event signals the insertion or deletion of an element + * + * @author Gabor Bergmann + */ +public enum Direction { + INSERT, DELETE; + + /** + * @since 2.4 + */ + public Direction opposite() { + switch (this) { + case INSERT: + return DELETE; + default: + return INSERT; + } + } + + /** + * @since 2.4 + */ + public char asSign() { + switch (this) { + case INSERT: + return '+'; + default: + return '-'; + } + } + + /** + * Returns the direction that is the product of this direction and the other direction. + * + * DELETE x DELETE = INSERT + * DELETE x INSERT = DELETE + * INSERT x DELETE = DELETE + * INSERT x INSERT = INSERT + * @since 2.4 + */ + public Direction multiply(final Direction other) { + switch (this) { + case DELETE: + return other.opposite(); + default: + return other; + } + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java new file mode 100644 index 00000000..e24b2448 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsBagMemory.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Iterator; +import java.util.Set; +import java.util.function.BiConsumer; + +import org.eclipse.collections.impl.map.mutable.primitive.ObjectIntHashMap; + +/** + * Eclipse Collections-based multiset for tuples. Can contain duplicate occurrences of the same matching. + * + *

Inherits Eclipse Collections' Object-to-Int primitive hashmap and counts the number of occurrences of each value. + * Element is deleted if # of occurences drops to 0. + * + * @author Gabor Bergmann. + * @since 1.7 + * @noreference + */ +public abstract class EclipseCollectionsBagMemory extends ObjectIntHashMap implements IMemory { + + public EclipseCollectionsBagMemory() { + super(); + } + + @Override + public int getCount(T value) { + return super.getIfAbsent(value, 0); + } + @Override + public int getCountUnsafe(Object value) { + return super.getIfAbsent(value, 0); + } + @Override + public boolean containsNonZero(T value) { + return super.containsKey(value); + } + @Override + public boolean containsNonZeroUnsafe(Object value) { + return super.containsKey(value); + } + + @Override + public void clearAllOf(T value) { + super.remove(value); + } + + + @Override + public Iterator iterator() { + return super.keySet().iterator(); + } + + @Override + public String toString() { + return "TM" + super.toString(); + } + + @Override + public Set distinctValues() { + return super.keySet(); + } + + @Override + public void forEachEntryWithMultiplicities(BiConsumer entryConsumer) { + super.forEachKeyValue(entryConsumer::accept); + } + + @Override + public int hashCode() { + return IMemoryView.hashCode(this); + } + @Override + public boolean equals(Object obj) { + return IMemoryView.equals(this, obj); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java new file mode 100644 index 00000000..94ec33cd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsDeltaBag.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +/** + * @author Gabor Bergmann + * @since 1.7 + */ +public class EclipseCollectionsDeltaBag extends EclipseCollectionsBagMemory implements IDeltaBag { + + @Override + public boolean addOne(T value) { + return addSigned(value, +1); + } + + @Override + public boolean addSigned(T value, int count) { + int oldCount = super.getIfAbsent(value, 0); + int newCount = oldCount + count; + + boolean becomesZero = newCount == 0; + if (becomesZero) + super.removeKey(value); + else + super.put(value, newCount); + + return becomesZero || oldCount == 0; + } + + + @Override + public boolean removeOne(T value) { + return addSigned(value, -1); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java new file mode 100644 index 00000000..5a623c9b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsFactory.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +import org.eclipse.collections.api.map.MutableMap; +import org.eclipse.collections.impl.factory.Maps; +import org.eclipse.collections.impl.factory.Sets; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.ICollectionsFramework; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; + +/** + * @author Gabor Bergmann + * @since 1.7 + * @noreference This class is not intended to be referenced by clients. + */ +public class EclipseCollectionsFactory implements ICollectionsFramework { + + @Override + public Map createMap() { + return Maps.mutable.empty(); + } + + @Override + public Map createMap(Map initial) { + MutableMap result = Maps.mutable.ofInitialCapacity(initial.size()); + result.putAll(initial); + return result; + } + + @Override + public TreeMap createTreeMap() { + // eclipse collections is doing the same + return new TreeMap<>(); + } + + @Override + public Set createSet() { + return Sets.mutable.empty(); + } + + @Override + public Set createSet(Collection initial) { + return Sets.mutable.ofAll(initial); + } + + @Override + public IMultiset createMultiset() { + return new EclipseCollectionsMultiset(); + } + + @Override + public IDeltaBag createDeltaBag() { + return new EclipseCollectionsDeltaBag(); + } + + @Override + public List createObserverList() { + return new ArrayList(1); // keep concurrent modification exceptions for error detection + // Lists.mutable.empty + + } + + @Override + @SuppressWarnings({ "unchecked", "rawtypes" }) + public IMultiLookup createMultiLookup( + Class fromKeys, + MemoryType toBuckets, + Class ofValues) + { + boolean longKeys = Long.class.equals(fromKeys); + boolean objectKeys = Object.class.equals(fromKeys); + if (! (longKeys || objectKeys)) throw new IllegalArgumentException(fromKeys.getName()); + boolean longValues = Long.class.equals(ofValues); + boolean objectValues = Object.class.equals(ofValues); + if (! (longValues || objectValues)) throw new IllegalArgumentException(ofValues.getName()); + + if (longKeys) { // K == java.lang.Long + if (longValues) { // V == java.lang.Long + switch(toBuckets) { + case MULTISETS: + return (IMultiLookup) new EclipseCollectionsMultiLookup.FromLongs.ToMultisets.OfLongs(); + case SETS: + return (IMultiLookup) new EclipseCollectionsMultiLookup.FromLongs.ToSets.OfLongs(); + default: + throw new IllegalArgumentException(toBuckets.toString()); + } + } else { // objectValues + switch(toBuckets) { + case MULTISETS: + return new EclipseCollectionsMultiLookup.FromLongs.ToMultisets.OfObjects(); + case SETS: + return new EclipseCollectionsMultiLookup.FromLongs.ToSets.OfObjects(); + default: + throw new IllegalArgumentException(toBuckets.toString()); + } + } + } else { // objectKeys + if (longValues) { // V == java.lang.Long + switch(toBuckets) { + case MULTISETS: + return new EclipseCollectionsMultiLookup.FromObjects.ToMultisets.OfLongs(); + case SETS: + return new EclipseCollectionsMultiLookup.FromObjects.ToSets.OfLongs(); + default: + throw new IllegalArgumentException(toBuckets.toString()); + } + } else { // objectValues + switch(toBuckets) { + case MULTISETS: + return new EclipseCollectionsMultiLookup.FromObjects.ToMultisets.OfObjects(); + case SETS: + return new EclipseCollectionsMultiLookup.FromObjects.ToSets.OfObjects(); + default: + throw new IllegalArgumentException(toBuckets.toString()); + } + } + } + } + + @Override + @SuppressWarnings("unchecked") + public IMemory createMemory(Class values, MemoryType memoryType) { + if (Long.class.equals(values)) { // T == java.lang.Long + switch(memoryType) { + case MULTISETS: + return (IMemory) new EclipseCollectionsLongMultiset(); + case SETS: + return (IMemory) new EclipseCollectionsLongSetMemory(); + default: + throw new IllegalArgumentException(memoryType.toString()); + } + } else { // objectValues + switch(memoryType) { + case MULTISETS: + return new EclipseCollectionsMultiset<>(); + case SETS: + return new EclipseCollectionsSetMemory<>(); + default: + throw new IllegalArgumentException(memoryType.toString()); + } + } + } + + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java new file mode 100644 index 00000000..88773c5d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongMultiset.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Iterator; +import java.util.Set; +import java.util.function.BiConsumer; + +import org.eclipse.collections.impl.map.mutable.primitive.LongIntHashMap; + +/** + * @author Gabor Bergmann + * @since 2.0 + *

TODO refactor common methods with {@link EclipseCollectionsMultiset} + *

TODO refactor into LongBagMemory etc. + */ +public class EclipseCollectionsLongMultiset extends LongIntHashMap implements IMultiset { + + @Override + public boolean addOne(Long value) { + int oldCount = super.getIfAbsent(value, 0); + + super.put(value, oldCount + 1); + + return oldCount == 0; + } + + @Override + public boolean addSigned(Long value, int count) { + int oldCount = super.getIfAbsent(value, 0); + int newCount = oldCount + count; + + boolean becomesZero = newCount == 0; + if (newCount < 0) + throw new IllegalStateException(String.format( + "Cannot remove %d occurrences of value '%s' as only %d would remain in %s", + count, value, newCount, this)); + else if (becomesZero) + super.removeKey(value); + else // (newCount > 0) + super.put(value, newCount); + + return becomesZero || oldCount == 0; + } + + @Override + public boolean removeOne(Long value) { + return removeOneInternal(value, true); + } + /** + * @since 2.3 + */ + @Override + public boolean removeOneOrNop(Long value) { + return removeOneInternal(value, false); + } + + + /** + * @since 2.3 + */ + protected boolean removeOneInternal(Long value, boolean throwIfImpossible) { + int oldCount = super.getIfAbsent(value, 0); + if (oldCount == 0) { + if (throwIfImpossible) throw new IllegalStateException(String.format( + "Cannot remove value '%s' that is not contained in %s", + value, this)); + else return false; + } + + int rest = oldCount - 1; + boolean empty = rest == 0; + + if (!empty) { + super.put(value, rest); + } else { + super.remove(value); + } + + return empty; + } + + @Override + public void clearAllOf(Long value) { + super.remove(value); + } + + @Override + public int getCount(Long value) { + return super.getIfAbsent(value, 0); + } + @Override + public int getCountUnsafe(Object value) { + return value instanceof Long ? getCount((Long) value) : 0; + } + + @Override + public boolean containsNonZero(Long value) { + return super.containsKey(value); + } + + @Override + public boolean containsNonZeroUnsafe(Object value) { + return value instanceof Long && containsNonZero((Long) value); + } + + @Override + public Iterator iterator() { + return EclipseCollectionsLongSetMemory.iteratorOf(super.keySet()); + } + + @Override + public boolean addPositive(Long value, int count) { + if (count < 0) { + throw new IllegalArgumentException("The count value must be positive!"); + } + + int oldCount = super.getIfAbsent(value, 0); + + super.put(value, oldCount + count); + + return oldCount == 0; + } + + @Override + public Set distinctValues() { + return new EclipseCollectionsLongSetMemory.SetWrapper(super.keySet()); + } + + @Override + public void forEachEntryWithMultiplicities(BiConsumer entryConsumer) { + super.forEachKeyValue(entryConsumer::accept); + } + + @Override + public int hashCode() { + return IMemoryView.hashCode(this); + } + @Override + public boolean equals(Object obj) { + return IMemoryView.equals(this, obj); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java new file mode 100644 index 00000000..fceb54fc --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsLongSetMemory.java @@ -0,0 +1,212 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Set; + +import org.eclipse.collections.api.LongIterable; +import org.eclipse.collections.api.iterator.LongIterator; +import org.eclipse.collections.api.set.primitive.LongSet; +import org.eclipse.collections.impl.set.mutable.primitive.LongHashSet; + +/** + * @author Gabor Bergmann + * @since 2.0 + */ +public class EclipseCollectionsLongSetMemory extends LongHashSet implements ISetMemory { + + @Override + public boolean addOne(Long value) { + return super.add(value); + } + + @Override + public boolean addSigned(Long value, int count) { + if (count == 1) return addOne(value); + else if (count == -1) return removeOne(value); + else throw new IllegalStateException(); + } + + @Override + public boolean removeOne(Long value) { + // Kept for binary compatibility + return ISetMemory.super.removeOne(value); + } + + /** + * @since 2.3 + */ + @Override + public boolean removeOneOrNop(Long value) { + return super.remove(value); + } + + @Override + public void clearAllOf(Long value) { + super.remove(value); + } + + @Override + public int getCount(Long value) { + return super.contains(value) ? 1 : 0; + } + + @Override + public int getCountUnsafe(Object value) { + return value instanceof Long ? getCount((Long) value) : 0; + } + + @Override + public boolean containsNonZero(Long value) { + return super.contains(value); + } + + @Override + public boolean containsNonZeroUnsafe(Object value) { + return value instanceof Long && containsNonZero((Long) value); + } + + @Override + public Iterator iterator() { + return iteratorOf(this); + } + + @Override + public Set distinctValues() { + return new SetWrapper(this); + } + + @Override + public boolean isEmpty() { + return super.isEmpty(); + } + + /** + * Helper for iterating a LongIterable + */ + public static Iterator iteratorOf(LongIterable wrapped) { + return new Iterator() { + LongIterator longIterator = wrapped.longIterator(); + + @Override + public boolean hasNext() { + return longIterator.hasNext(); + } + + @Override + public Long next() { + return longIterator.next(); + } + }; + } + + @Override + public int hashCode() { + return IMemoryView.hashCode(this); + } + @Override + public boolean equals(Object obj) { + return IMemoryView.equals(this, obj); + } + + + /** + * Helper that presents a primitive collection as a Set view + * @author Gabor Bergmann + */ + public static final class SetWrapper implements Set { + private LongSet wrapped; + + /** + * @param wrapped + */ + public SetWrapper(LongSet wrapped) { + this.wrapped = wrapped; + } + + @Override + public int size() { + return wrapped.size(); + } + + @Override + public boolean isEmpty() { + return wrapped.isEmpty(); + } + + @Override + public boolean contains(Object o) { + return o instanceof Long && wrapped.contains((Long)o); + } + + @Override + public Iterator iterator() { + return iteratorOf(wrapped); + } + + @Override + public boolean containsAll(Collection c) { + for (Object object : c) { + if (contains(object)) + return true; + } + return false; + } + + @Override + public Object[] toArray() { + return toArray(new Long[wrapped.size()]); + } + + @Override + @SuppressWarnings("unchecked") + public T[] toArray(T[] a) { + int k = 0; + LongIterator iterator = wrapped.longIterator(); + while (iterator.hasNext()) + a[k++] = (T) Long.valueOf(iterator.next()); + return a; + } + + @Override + public boolean add(Long e) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java new file mode 100644 index 00000000..394135c9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiLookup.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import org.eclipse.collections.impl.map.mutable.UnifiedMap; +import org.eclipse.collections.impl.map.mutable.primitive.LongObjectHashMap; +import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedMultiset; +import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet; + +import java.util.Set; +import java.util.stream.Stream; + + + +/** + * Eclipse Collections-based realizations of {@link IMultiLookup} + * + * @author Gabor Bergmann + * @since 2.0 + */ +class EclipseCollectionsMultiLookup { + + private EclipseCollectionsMultiLookup() {/* Hidden utility class constructor */} + + private static class MarkedSetImpl extends EclipseCollectionsSetMemory implements MarkedMemory.MarkedSet {} + private static class MarkedMultisetImpl extends EclipseCollectionsMultiset implements MarkedMemory.MarkedMultiset {} + private static class MarkedLongSetImpl extends EclipseCollectionsLongSetMemory implements MarkedMemory.MarkedSet {} + private static class MarkedLongMultisetImpl extends EclipseCollectionsLongMultiset implements MarkedMemory.MarkedMultiset {} + + public abstract static class FromObjects> + extends UnifiedMap implements IMultiLookupAbstract { + + @Override + public boolean equals(Object obj) { + return IMultiLookup.equals(this, obj); + } + @Override + public int hashCode() { + return IMultiLookup.hashCode(this); + } + + + @Override + public Object lowLevelPutIfAbsent(Key key, Value value) { + return super.putIfAbsent(key, value); + } + + @Override + public Object lowLevelGet(Key key) { + return super.get(key); + } + + @Override + public Object lowLevelGetUnsafe(Object key) { + return super.get(key); + } + + @Override + public Object lowLevelRemove(Key key) { + return super.remove(key); + } + + @Override + public void lowLevelPut(Key key, Object valueOrBucket) { + super.put(key, valueOrBucket); + } + @Override + public Iterable lowLevelValues() { + return super.values(); + } + @Override + public Set lowLevelKeySet() { + return super.keySet(); + } + @Override + public int lowLevelSize() { + return super.size(); + } + + @Override + public Stream distinctKeysStream() { + // may be more efficient than the default spliterator + return super.keySet().stream(); + } + + public abstract static class ToSets extends FromObjects> + implements IMultiLookupAbstract.ToSetsAbstract + { + public static class OfObjects extends ToSets { + @Override + public MarkedSet createMarkedSet() { + return new MarkedSetImpl(); + } + } + + public static class OfLongs extends ToSets { + @Override + public MarkedSet createMarkedSet() { + return new MarkedLongSetImpl(); + } + } + + } + + public abstract static class ToMultisets extends FromObjects> + implements IMultiLookupAbstract.ToMultisetsAbstract + { + public static class OfObjects extends ToMultisets { + @Override + public MarkedMultiset createMarkedMultiset() { + return new MarkedMultisetImpl(); + } + } + + public static class OfLongs extends ToMultisets { + @Override + public MarkedMultiset createMarkedMultiset() { + return new MarkedLongMultisetImpl(); + } + } + + } + + } + + public abstract static class FromLongs> + extends LongObjectHashMap implements IMultiLookupAbstract { + + @Override + public boolean equals(Object obj) { + return IMultiLookup.equals(this, obj); + } + @Override + public int hashCode() { + return IMultiLookup.hashCode(this); + } + + @Override + public Object lowLevelPutIfAbsent(Long key, Value value) { + Object old = super.get(key); + if (old == null) super.put(key, value); + return old; + } + + @Override + public Object lowLevelGet(Long key) { + return super.get(key); + } + + @Override + public Object lowLevelGetUnsafe(Object key) { + return key instanceof Long ? super.get((Long)key) : null; + } + + @Override + public Object lowLevelRemove(Long key) { + return super.remove(key); + } + + @Override + public void lowLevelPut(Long key, Object valueOrBucket) { + super.put(key, valueOrBucket); + } + @Override + public Iterable lowLevelValues() { + return super.values(); + } + @Override + public int lowLevelSize() { + return super.size(); + } + @Override + public Iterable lowLevelKeySet() { + return () -> EclipseCollectionsLongSetMemory.iteratorOf(FromLongs.super.keysView()); + } + + public abstract static class ToSets extends FromLongs> + implements IMultiLookupAbstract.ToSetsAbstract + { + public static class OfObjects extends ToSets { + @Override + public MarkedSet createMarkedSet() { + return new MarkedSetImpl(); + } + } + + public static class OfLongs extends ToSets { + @Override + public MarkedSet createMarkedSet() { + return new MarkedLongSetImpl(); + } + } + + } + + public abstract static class ToMultisets extends FromLongs> + implements IMultiLookupAbstract.ToMultisetsAbstract + { + public static class OfObjects extends ToMultisets { + @Override + public MarkedMultiset createMarkedMultiset() { + return new MarkedMultisetImpl(); + } + } + + public static class OfLongs extends ToMultisets { + @Override + public MarkedMultiset createMarkedMultiset() { + return new MarkedLongMultisetImpl(); + } + } + + } + + } + + +} + + diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java new file mode 100644 index 00000000..46977c8b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsMultiset.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +/** + * @author Gabor Bergmann + * @since 1.7 + */ +public class EclipseCollectionsMultiset extends EclipseCollectionsBagMemory implements IMultiset { + + @Override + public boolean addOne(T value) { + int oldCount = super.getIfAbsent(value, 0); + + super.put(value, oldCount + 1); + + return oldCount == 0; + } + + @Override + public boolean addPositive(T value, int count) { + if (count < 0) { + throw new IllegalArgumentException("The count value must be positive!"); + } + + int oldCount = super.getIfAbsent(value, 0); + + super.put(value, oldCount + count); + + return oldCount == 0; + } + + @Override + public boolean addSigned(T value, int count) { + int oldCount = super.getIfAbsent(value, 0); + int newCount = oldCount + count; + + boolean becomesZero = newCount == 0; + if (newCount < 0) + throw new IllegalStateException(String.format( + "Cannot remove %d occurrences of value '%s' as only %d would remain in %s", + count, value, newCount, this)); + else if (becomesZero) + super.removeKey(value); + else // (newCount > 0) + super.put(value, newCount); + + return becomesZero || oldCount == 0; + } + + + @Override + public boolean removeOne(T value) { + return removeOneInternal(value, true); + } + + @Override + public boolean removeOneOrNop(T value) { + return removeOneInternal(value, false); + } + + /** + * @since 2.3 + */ + protected boolean removeOneInternal(T value, boolean throwIfImpossible) { + int oldCount = super.getIfAbsent(value, 0); + if (oldCount == 0) { + if (throwIfImpossible) throw new IllegalStateException(String.format( + "Cannot remove value '%s' that is not contained in %s", + value, this)); + else return false; + } + + int rest = oldCount - 1; + boolean empty = rest == 0; + + if (!empty) { + super.put(value, rest); + } else { + super.remove(value); + } + + return empty; + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java new file mode 100644 index 00000000..92f65246 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EclipseCollectionsSetMemory.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Set; + +import org.eclipse.collections.impl.set.mutable.UnifiedSet; + +/** + * @author Gabor Bergmann + * @since 2.0 + */ +public class EclipseCollectionsSetMemory extends UnifiedSet implements ISetMemory { + @Override + public int getCount(Value value) { + return super.contains(value) ? 1 : 0; + } + @Override + public int getCountUnsafe(Object value) { + return super.contains(value) ? 1 : 0; + } + @Override + public boolean containsNonZero(Value value) { + return super.contains(value); + } + + @Override + public boolean containsNonZeroUnsafe(Object value) { + return super.contains(value); + } + + @Override + public boolean addOne(Value value) { + return super.add(value); + } + + @Override + public boolean addSigned(Value value, int count) { + if (count == 1) return addOne(value); + else if (count == -1) return removeOne(value); + else throw new IllegalStateException(); + } + + @Override + public boolean removeOne(Value value) { + // Kept for binary compatibility + return ISetMemory.super.removeOne(value); + } + + @Override + public boolean removeOneOrNop(Value value) { + return super.remove(value); + } + + @Override + public void clearAllOf(Value value) { + super.remove(value); + } + + @Override + public Set distinctValues() { + return this; + } + + @Override + public Value theContainedVersionOf(Value value) { + return super.get(value); + } + + @Override + @SuppressWarnings("unchecked") + public Value theContainedVersionOfUnsafe(Object value) { + if (super.contains(value)) + return super.get((Value)value); + else return null; + } + + @Override + public int hashCode() { + return IMemoryView.hashCode(this); + } + @Override + public boolean equals(Object obj) { + return IMemoryView.equals(this, obj); + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java new file mode 100644 index 00000000..a17b3a3f --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/EmptyMemory.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; + +/** + * A singleton immutable empty memory. + * @author Gabor Bergmann + * @since 2.0 + * + */ +public class EmptyMemory implements IMemoryView { + + @SuppressWarnings("rawtypes") + private static final EmptyMemory INSTANCE = new EmptyMemory(); + + @SuppressWarnings("unchecked") + public static EmptyMemory instance() { + return INSTANCE; + } + + + + /** + * Singleton; hidden constructor + */ + private EmptyMemory() { + super(); + } + + @Override + public Iterator iterator() { + return Collections.emptySet().iterator(); + } + + @Override + public int getCount(T value) { + return 0; + } + + @Override + public int getCountUnsafe(Object value) { + return 0; + } + + @Override + public boolean containsNonZero(T value) { + return false; + } + + @Override + public boolean containsNonZeroUnsafe(Object value) { + return false; + } + + @Override + public int size() { + return 0; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public Set distinctValues() { + return Collections.emptySet(); + } + + @Override + public int hashCode() { + return IMemoryView.hashCode(this); + } + @Override + public boolean equals(Object obj) { + return IMemoryView.equals(this, obj); + } + + @Override + public String toString() { + return "{}"; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java new file mode 100644 index 00000000..8c2e54ad --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ICache.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.function.Supplier; + +/** + * A cache is a simple key-value pair that stores calculated values for specific key objects + * + *

+ * NOTE These caches are not expected to be used outside query backend implementations + * + * @author Zoltan Ujhelyi + * @since 1.7 + * @noreference This interface is not intended to be referenced by clients. + */ +public interface ICache { + + /** + * Return a selected value for the key object. If the value is not available in the cache yet, the given provider is + * called once + * @since 2.0 + */ + T getValue(Object key, Class clazz, Supplier valueProvider); + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java new file mode 100644 index 00000000..99a4cb3b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IDeltaBag.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +/** + * An {@link IMemory} that represents the difference between two states of a set or {@link IMultiset}, and therefore + * may contain values with a negative multiplicity. + * + * @author Gabor Bergmann + * @since 1.7 + */ +public interface IDeltaBag extends IMemory { + + @Override + default boolean removeOneOrNop(T value) { + // makes no difference for delta bags + return removeOne(value); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java new file mode 100644 index 00000000..ea788e53 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemory.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +/** + * A memory containing a positive or negative number of equal() copies for some values. + * During iterations, each distinct value is iterated only once. + * + *

Refined by:

    + *
  • {@link IMultiset}, which always contains values with a nonnegative multiplicity.
  • + *
  • {@link IDeltaBag}, which may contain values with negative multiplicity.
  • + *
  • {@link ISetMemory}, which is just a set (allowed multiplicities: 0 and 1).
  • + *
+ * + * @author Gabor Bergmann + * @since 1.7 + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IMemory extends IMemoryView, Clearable { + + /** + * Adds one value occurrence to the memory. + * + * @return true if the tuple was not present before in the memory, or + * (in case of {@link IDeltaBag}) is no longer present in the memory + */ + boolean addOne(T value); + + /** + * Adds the given number of occurrences to the memory. The count value may or may not be negative. + *

Precondition if {@link IMultiset}: at least the given amount of occurrences exist, if count is negative. + *

Precondition if {@link ISetMemory}: count is +1 or -1, the latter is only allowed if the set contains the value. + * + * @param count + * the number of occurrences + * @return true if the tuple was not present before in the memory, or is no longer present in the memory + * @throws IllegalStateException if {@link IMultiset} or {@link ISetMemory} and the number of occurrences in the memory would underflow to negative + */ + boolean addSigned(T value, int count); + + /** + * Removes one occurrence of the given value from the memory. + *

Precondition if {@link IMultiset} or {@link ISetMemory}: the value must have a positive amount of occurrences in the memory. + * + * @return true if this was the the last occurrence of the value, or + * (in case of {@link IDeltaBag}) is the first negative occurrence of the value + * @throws IllegalStateException if {@link IMultiset} or {@link ISetMemory} and value had no occurrences in the memory + */ + boolean removeOne(T value); + + /** + * Removes one occurrence of the given value from the memory, if possible. + * + *

Memory is unchanged and false is returned if + * {@link IMultiset} or {@link ISetMemory} and value had no occurrences in the memory + * + * @return true if this was the the last occurrence of the value, or + * (in case of {@link IDeltaBag}) is the first negative occurrence of the value + * + * @since 2.3 + */ + boolean removeOneOrNop(T value); + + /** + * Removes all occurrences of the given value from the memory. + */ + void clearAllOf(T value); + + /** + * Empties out the memory. + */ + @Override + void clear(); + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java new file mode 100644 index 00000000..add575c6 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMemoryView.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +/** + * A read-only view on a memory containing a positive or negative number of equal() copies for some values. + * During iterations, each distinct value is iterated only once. + * + *

See {@link IMemory}. + * + *

Implementors must provide semantic (not identity-based) hashCode() and equals() using the static helpers {@link #hashCode(IMemoryView)} and {@link #equals(IMemoryView, Object)} here. + * + * @author Gabor Bergmann + * + * @since 2.0 + */ +public interface IMemoryView extends Iterable { + + /** + * Returns the number of occurrences of the given value. + * + * @return the number of occurrences + */ + int getCount(T value); + + /** + * Returns the number of occurrences of the given value (which may be of any type). + * + * @return the number of occurrences + */ + int getCountUnsafe(Object value); + + /** + * @return true if the given value is contained with a nonzero multiplicity + */ + boolean containsNonZero(T value); + + /** + * @return true if the given value (which may be of any type) is contained with a nonzero multiplicity + */ + boolean containsNonZeroUnsafe(Object value); + + /** + * @return the number of distinct values + */ + int size(); + + /** + * + * @return iff contains at least one value with non-zero occurrences + */ + boolean isEmpty(); + + /** + * The set of distinct values + */ + Set distinctValues(); + + + /** + * Where supported, returns the stored element that is equal to the given value, or null if none. + * Useful for canonicalization in case of non-identity equals(). + * + *

For collections that do not support canonicalization, simply returns the argument if contained, null if none. + * + * @return a value equal to the argument if such a value is stored, or null if none + */ + default T theContainedVersionOf(T value) { + if (containsNonZero(value)) return value; else return null; + } + + /** + * Where supported, returns the stored element that is equal to the given value (of any type), + * or null if none. + * Useful for canonicalization in case of non-identity equals(). + * + *

For collections that do not support canonicalization, simply returns the argument if contained, null if none. + * + * @return a value equal to the argument if such a value is stored, or null if none + */ + @SuppressWarnings("unchecked") + default T theContainedVersionOfUnsafe(Object value) { + if (containsNonZeroUnsafe(value)) return (T) value; else return null; + } + + + /** + * @return an unmodifiable view of contained values with their multiplicities + */ + default Iterable> entriesWithMultiplicities() { + return () -> { + Iterator wrapped = distinctValues().iterator(); + return new Iterator> () { + @Override + public boolean hasNext() { + return wrapped.hasNext(); + } + + @Override + public Map.Entry next() { + T key = wrapped.next(); + int count = getCount(key); + return new Map.Entry(){ + @Override + public T getKey() { + return key; + } + + @Override + public Integer getValue() { + return count; + } + + @Override + public Integer setValue(Integer value) { + throw new UnsupportedOperationException(); + } + + @Override + public String toString() { + return String.format("%d of %s", count, key); + } + + }; + } + + }; + }; + } + + /** + * Process contained values with their multiplicities + */ + default void forEachEntryWithMultiplicities(BiConsumer entryConsumer) { + for (T value : distinctValues()) { + entryConsumer.accept(value, getCount(value)); + } + } + + + /** + * For compatibility with legacy code relying on element-to-integer maps. + * @return an unmodifiable view of contained values with their multiplicities + */ + public default Map asMap() { + return new MemoryViewBackedMapView<>(this); + } + + /** + * For compatibility with legacy code relying on element-to-integer maps. + * @return an unmodifiable view of contained values with their multiplicities + */ + public static IMemoryView fromMap(Map wrapped) { + return new MapBackedMemoryView<>(wrapped); + } + + /** + * @return a stream of values, iterable once + * @since 2.1 + */ + public default Stream asStream() { + return StreamSupport.stream(spliterator(), false); + } + + /** + * Provides semantic equality comparison. + */ + public static boolean equals(IMemoryView self, Object obj) { + if (obj instanceof IMemoryView) { + IMemoryView other = (IMemoryView) obj; + if (other.size() != self.size()) return false; + for (Entry entry : other.entriesWithMultiplicities()) { + if ( !entry.getValue().equals(self.getCountUnsafe(entry.getKey()))) + return false; + } + return true; + } + return false; + } + + /** + * Provides semantic hashCode() comparison. + */ + public static int hashCode(IMemoryView memory) { + int hashCode = 0; + for (T value : memory.distinctValues()) { + hashCode += value.hashCode() ^ Integer.hashCode(memory.getCount(value)); + } + return hashCode; + } +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java new file mode 100644 index 00000000..1ce1d2c9 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookup.java @@ -0,0 +1,216 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.stream.Stream; + +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; + +/** + * A multi-map that associates sets / multisets / delta sets of values to each key. + * + *

Implementors must provide semantic (not identity-based) hashCode() and equals() using the static helpers {@link #hashCode(IMultiLookup)} and {@link #equals(IMultiLookup, Object)} here. + * + * @author Gabor Bergmann + * @since 2.0 + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface IMultiLookup { + + /** + * Returns true if this collection is empty, false otherwise. + * @since 2.2 + */ + boolean isEmpty(); + + + /** + * Returns true if there are any values associated with the given key. + * @param key a key for which associated values are sought + * @since 2.3 + */ + boolean lookupExists(Key key); + + /** + * Returns a (read-only) bucket of values associated with the given key. + * Clients must not modify the returned bucket. + * @param key a key for which associated values are sought + * @return null if key not found, a bucket of values otherwise + */ + IMemoryView lookup(Key key); + + /** + * Returns a (read-only) bucket of values associated with the given key. + * Clients must not modify the returned bucket. + * @param key a key for which associated values are sought + * @return a bucket of values, never null + */ + default IMemoryView lookupOrEmpty(Key key) { + IMemoryView bucket = lookup(key); + return bucket == null ? EmptyMemory.instance() : bucket; + } + + /** + * Returns a (read-only) bucket of values associated with the given key, while simultaneously removing them. + * Clients must not modify the returned bucket. + * @param key a key for which associated values are sought + * @return a bucket of values, never null + * @since 2.3 + */ + IMemoryView lookupAndRemoveAll(Key key); + + /** + * Returns a (read-only) bucket of values associated with the given key, which can be of any type. + * Clients must not modify the returned bucket. + * @param key a key for which associated values are sought (may or may not be of Key type) + * @return null if key not found, a bucket of values otherwise + */ + IMemoryView lookupUnsafe(Object key); + + /** + * Returns a (read-only) bucket of values associated with the given key. + * Clients must not modify the returned bucket. + * @param key a key for which associated values are sought (may or may not be of Key type) + * @return a bucket of values, never null + */ + default IMemoryView lookupUnsafeOrEmpty(Object key) { + IMemoryView bucket = lookupUnsafe(key); + return bucket == null ? EmptyMemory.instance() : bucket; + } + + + + /** + * @return the set of distinct keys that have values associated. + */ + Iterable distinctKeys(); + + /** + * @return the set of distinct keys that have values associated. + * @since 2.3 + */ + Stream distinctKeysStream(); + + /** + * @return the number of distinct keys that have values associated. + */ + int countKeys(); + + /** + * Iterates once over each distinct value. + */ + Iterable distinctValues(); + + /** + * Iterates once over each distinct value. + * @since 2.3 + */ + Stream distinctValuesStream(); + + + + /** + * How significant was the change? * + * @author Gabor Bergmann + */ + public enum ChangeGranularity { + /** + * First key-value pair with given key inserted, or last pair with given key deleted. + * (In case of delta maps, also if last negative key-value pair with given key neutralized.) + */ + KEY, + /** + * First occurrence of given key-value pair inserted, or last occurrence of the pair deleted, while key still has values associated. + * (In case of delta maps, also if last negative occurrence of key-value pair neutralized.) + */ + VALUE, + /** + * Duplicate key-value pair inserted or deleted. + */ + DUPLICATE + } + + /** + * Adds key-value pair to the lookup structure, or fails if not possible. + *

If the addition would cause duplicates but the bucket type does not allow it ({@link MemoryType#SETS}), + * the operation throws an {@link IllegalStateException}. + * @return the granularity of the change + * @throws IllegalStateException if addition would cause duplication that is not permitted + */ + public ChangeGranularity addPair(Key key, Value value); + /** + * Adds key-value pair to the lookup structure. + *

If the addition would cause duplicates but the bucket type does not allow it ({@link MemoryType#SETS}), + * the operation is silently ignored and {@link ChangeGranularity#DUPLICATE} is returned. + * @return the granularity of the change, or {@link ChangeGranularity#DUPLICATE} if addition would result in a duplicate and therefore ignored + * @since 2.3 + */ + public ChangeGranularity addPairOrNop(Key key, Value value); + /** + * Removes key-value pair from the lookup structure, or fails if not possible. + *

When attempting to remove a key-value pair with zero multiplicity from a non-delta bucket type + * ({@link MemoryType#SETS} or {@link MemoryType#MULTISETS}}), an {@link IllegalStateException} is thrown. + * @return the granularity of the change + * @throws IllegalStateException if removing non-existing element that is not permitted + */ + public ChangeGranularity removePair(Key key, Value value); + /** + * Removes key-value pair from the lookup structure. + *

When attempting to remove a key-value pair with zero multiplicity from a non-delta bucket type + * ({@link MemoryType#SETS} or {@link MemoryType#MULTISETS}}), + * the operation is silently ignored and {@link ChangeGranularity#DUPLICATE} is returned. + * @return the granularity of the change + * @throws IllegalStateException if removing non-existing element that is not permitted + * @since 2.3 + */ + public ChangeGranularity removePairOrNop(Key key, Value value); + + /** + * Updates multiplicity of key-value pair by a positive amount. + * + *

PRE: count > 0 + * + * @return the granularity of the change + * @throws IllegalStateException if addition would cause duplication that is not permitted + */ + public ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count); + + /** + * Empties out the lookup structure. + */ + public void clear(); + + /** + * Provides semantic equality comparison. + */ + public static boolean equals(IMultiLookup self, Object obj) { + if (obj instanceof IMultiLookup) { + IMultiLookup other = (IMultiLookup) obj; + if (other.countKeys() != self.countKeys()) return false; + for (Object key : other.distinctKeys()) { + if (! other.lookupUnsafe(key).equals(self.lookupUnsafe(key))) + return false; + } + return true; + } + return false; + } + + /** + * Provides semantic hashCode() comparison. + */ + public static int hashCode(IMultiLookup memory) { + int hashCode = 0; + for (Key key : memory.distinctKeys()) { + hashCode += key.hashCode() ^ memory.lookup(key).hashCode(); + } + return hashCode; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java new file mode 100644 index 00000000..8b1944c1 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiLookupAbstract.java @@ -0,0 +1,485 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +import tools.refinery.viatra.runtime.matchers.util.MarkedMemory.MarkedSet; + +/** + * Specialized multimap implementation that saves memory + * by storing singleton value objects (multiplicity 1) instead of multiset buckets + * whenever there is only one value associated with a key. + * + *

See specialized {@link ToSetsAbstract}, {@link ToMultisetsAbstract} for various bucket types. + * + *

Implemented as a Key->Object map with invariant:

    + *
  • key maps to null if associated with no values; + *
  • key maps to a single Value iff it is associated with a single value of multiplicity +1; + *
  • key maps to Bucket otherwise + *
+ * + * Note that due to the above invariant, handling +1 and -1 are asymmetric in case of delta maps. + * + *

Not intended as an API, but rather as a 'base class' for implementors. + * Realized as an interface with default implementations, instead of an abstract class, + * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. + * + *

Implementor should inherit from a Map-like class (primitive map possible) + * and bind the lowLevel* methods accordingly. + * + * @noreference This interface is not intended to be referenced by clients. + * @noimplement This interface is not intended to be implemented by clients. + * + * @author Gabor Bergmann + * @since 2.0 + * + * + */ +public interface IMultiLookupAbstract> extends IMultiLookup { + + // the following methods must be bound to a concrete Map-like structure (primitive implementation allowed) + + /** + * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map + */ + abstract Object lowLevelGet(Key key); + + /** + * Implementor shall bind to the low-level get() or equivalent of the underlying Key-to-Object map + */ + abstract Object lowLevelGetUnsafe(Object key); + + /** + * Implementor shall bind to the low-level remove() or equivalent of the underlying Key-to-Object map + */ + abstract Object lowLevelRemove(Key key); + + /** + * Implementor shall bind to the low-level putIfAbsent() or equivalent of the underlying Key-to-Object map + */ + abstract Object lowLevelPutIfAbsent(Key key, Value value); + + /** + * Implementor shall bind to the low-level put() or equivalent of the underlying Key-to-Object map + */ + abstract void lowLevelPut(Key key, Object valueOrBucket); + + /** + * Implementor shall bind to the low-level values() or equivalent of the underlying Key-to-Object map + */ + abstract Iterable lowLevelValues(); + + /** + * Implementor shall bind to the low-level keySet() or equivalent of the underlying Key-to-Object map + */ + abstract Iterable lowLevelKeySet(); + + /** + * Implementor shall bind to the low-level size() or equivalent of the underlying Key-to-Object map + */ + abstract int lowLevelSize(); + + + // generic multi-lookup logic + + @Override + default boolean lookupExists(Key key) { + Object object = lowLevelGet(key); + return null != object; + } + + @Override + public default IMemoryView lookup(Key key) { + Object object = lowLevelGet(key); + if (object == null) return null; + if (object instanceof MarkedMemory) return (Bucket) object; + return yieldSingleton((Value)object); + } + + @Override + default IMemoryView lookupAndRemoveAll(Key key) { + Object object = lowLevelRemove(key); + if (object == null) return EmptyMemory.instance(); + if (object instanceof MarkedMemory) return (Bucket) object; + return yieldSingleton((Value)object); + } + + @Override + public default IMemoryView lookupUnsafe(Object key) { + Object object = lowLevelGetUnsafe(key); + if (object == null) return null; + if (object instanceof MarkedMemory) return (Bucket) object; + return yieldSingleton((Value)object); + } + + @Override + public default ChangeGranularity addPair(Key key, Value value) { + return addPairInternal(key, value, true); + } + + @Override + default ChangeGranularity addPairOrNop(Key key, Value value) { + return addPairInternal(key, value, false); + } + + public default ChangeGranularity addPairInternal(Key key, Value value, boolean throwIfImpossible) { + Object old = lowLevelPutIfAbsent(key, value); + boolean keyChange = (old == null); + + if (keyChange) { // key was not present + return ChangeGranularity.KEY; + } else { // key was already present + Bucket bucket; + if (old instanceof MarkedMemory) { // ... as collection + bucket = (Bucket) old; + } else { // ... as singleton + if (!this.duplicatesAllowed() && Objects.equals(value, old)) { + if (throwIfImpossible) + throw new IllegalStateException(); + else + return ChangeGranularity.DUPLICATE; + } + bucket = createSingletonBucket((Value) old); + lowLevelPut(key, bucket); + } + // will throw if forbidden duplicate, return false if allowed duplicate + if (addToBucket(bucket, value, throwIfImpossible)) { + // deltas may become empty or a singleton after addition! + if (negativesAllowed()) { + if (bucket.isEmpty()) { + lowLevelRemove(key); + return ChangeGranularity.KEY; + } else { + handleSingleton(key, bucket); + return ChangeGranularity.VALUE; + } + } else return ChangeGranularity.VALUE; + } else return ChangeGranularity.DUPLICATE; + } + } + + @Override + // TODO deltas not supproted yet + default ChangeGranularity addPairPositiveMultiplicity(Key key, Value value, int count) { + if (count == 1) return addPair(key, value); + // count > 1, always end up with non-singleton bucket + + Object old = lowLevelGet(key); + boolean keyChange = (old == null); + + Bucket bucket; + if (keyChange) { // ... nothing associated to key yet + bucket = createSingletonBucket(value); + lowLevelPut(key, bucket); + --count; // one less to increment later + } else if (old instanceof MarkedMemory) { // ... as collection + bucket = (Bucket) old; + } else { // ... as singleton + bucket = createSingletonBucket((Value) old); + lowLevelPut(key, bucket); + } + + boolean newValue = bucket.addSigned(value, count); + + if (keyChange) return ChangeGranularity.KEY; + else if (newValue) return ChangeGranularity.VALUE; + else return ChangeGranularity.DUPLICATE; + } + + @Override + public default ChangeGranularity removePair(Key key, Value value) { + return removePairInternal(key, value, true); + } + + @Override + default ChangeGranularity removePairOrNop(Key key, Value value) { + return removePairInternal(key, value, false); + } + + public default ChangeGranularity removePairInternal(Key key, Value value, boolean throwIfImpossible) { + Object old = lowLevelGet(key); + if (old instanceof MarkedMemory) { // ... as collection + @SuppressWarnings("unchecked") + Bucket bucket = (Bucket) old; + // will throw if removing non-existent, return false if removing duplicate + boolean valueChange = removeFromBucket(bucket, value, throwIfImpossible); + handleSingleton(key, bucket); + if (valueChange) + return ChangeGranularity.VALUE; + else + return ChangeGranularity.DUPLICATE; + } else if (value.equals(old)) { // matching singleton + lowLevelRemove(key); + return ChangeGranularity.KEY; + } else { // different singleton, will produce a delta if possible + if (negativesAllowed()) { + Bucket deltaBucket = createDeltaBucket((Value) old, value); // will throw if no deltas supported + lowLevelPut(key, deltaBucket); + return ChangeGranularity.VALUE; // no key change + } else { + if (throwIfImpossible) + throw new IllegalStateException(); + else + return ChangeGranularity.DUPLICATE; + } + } + } + + public default void handleSingleton(Key key, Bucket bucket) { + Value remainingSingleton = asSingleton(bucket); + if (remainingSingleton != null) { // only one remains + lowLevelPut(key, remainingSingleton); + } + } + + @Override + public default Iterable distinctValues() { + return new Iterable() { + private final Iterator EMPTY_ITERATOR = Collections.emptySet().iterator(); + @Override + public Iterator iterator() { + return new Iterator() { + Iterator bucketIterator = lowLevelValues().iterator(); + Iterator elementIterator = EMPTY_ITERATOR; + + @Override + public boolean hasNext() { + return (elementIterator.hasNext() || bucketIterator.hasNext()); + } + + @Override + public Value next() { + if (elementIterator.hasNext()) + return elementIterator.next(); + else if (bucketIterator.hasNext()) { + Object bucket = bucketIterator.next(); + if (bucket instanceof MarkedMemory) { + elementIterator = + ((MarkedMemory) bucket).distinctValues().iterator(); + return elementIterator.next(); + } else { + elementIterator = EMPTY_ITERATOR; + return (Value) bucket; + } + } else + throw new NoSuchElementException(); + } + + /** + * Not implemented + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + + }; + } + }; + } + + @Override + default Stream distinctValuesStream() { + return StreamSupport.stream(distinctValues().spliterator(), false); + } + + @Override + default Iterable distinctKeys() { + return lowLevelKeySet(); + } + + @Override + default Stream distinctKeysStream() { + return StreamSupport.stream(distinctKeys().spliterator(), false); + } + + @Override + default int countKeys() { + return lowLevelSize(); + } + + // the following methods are customized for bucket type + + /** + * @return iff negative multiplicites are allowed + */ + abstract boolean negativesAllowed(); + + /** + * @return iff larger-than-1 multiplicites are allowed + * @since 2.3 + */ + abstract boolean duplicatesAllowed(); + + /** + * Increases the multiplicity of the value in the bucket. + * @return true iff non-duplicate + * @throws IllegalStateException if disallowed duplication and throwIfImpossible is specified + */ + abstract boolean addToBucket(Bucket bucket, Value value, boolean throwIfImpossible); + + /** + * Decreases the multiplicity of the value in the bucket. + * @return false if removing duplicate value + * @throws IllegalStateException if removing non-existing value (unless delta map) and throwIfImpossible is specified + */ + abstract boolean removeFromBucket(Bucket bucket, Value value, boolean throwIfImpossible); + + /** + * Checks whether the bucket is a singleton, i.e. it contains a single value with multiplicity +1 + * @return the singleton value, or null if the bucket is not singleton + */ + abstract Value asSingleton(Bucket bucket); + + /** + * @return a new bucket consisting of a sole value + */ + abstract Bucket createSingletonBucket(Value value); + /** + * @return a read-only bucket consisting of a sole value, to be returned to the user + */ + default IMemoryView yieldSingleton(Value value) { + return new SingletonMemoryView<>(value); + } + + /** + * @param positive the previously existing value, or null if the delta is to contain a single negative tuple + * @return a new bucket consisting of a delta of two values + * @throws IllegalStateException if deltas not supported + */ + abstract Bucket createDeltaBucket(Value positive, Value negative); + + /** + * A multi-lookup whose buckets are sets. + * + *

Not intended as an API, but rather as a 'base class' for implementors. + * Realized as an interface with default implementations, instead of an abstract class, + * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. + * + *

Implementor should inherit from a Map-like class (primitive map possible) + * and bind the lowLevel* methods accordingly. + * + * @noreference This interface is not intended to be referenced by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @author Gabor Bergmann + */ + public static interface ToSetsAbstract extends IMultiLookupAbstract> { + /** + * @return a fresh, empty marked set + */ + public MarkedSet createMarkedSet(); + + @Override + public default boolean negativesAllowed() { + return false; + } + @Override + default boolean duplicatesAllowed() { + return false; + } + + @Override + public default boolean addToBucket(MarkedSet bucket, Value value, boolean throwIfImpossible) { + if (bucket.addOne(value)) return true; + else if (throwIfImpossible) throw new IllegalStateException(); + else return false; + } + + @Override + public default boolean removeFromBucket(MarkedSet bucket, Value value, boolean throwIfImpossible) { + return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value); + } + + @Override + public default Value asSingleton(MarkedSet bucket) { + return bucket.size() == 1 ? bucket.iterator().next() : null; + } + + @Override + public default MarkedSet createSingletonBucket(Value value) { + MarkedSet result = createMarkedSet(); + result.addOne(value); + return result; + } + + @Override + public default MarkedSet createDeltaBucket(Value positive, Value negative) { + throw new IllegalStateException(); + } + } + + /** + * A multi-lookup whose buckets are multisets. + * + *

Not intended as an API, but rather as a 'base class' for implementors. + * Realized as an interface with default implementations, instead of an abstract class, + * to ensure that implementors can easily choose a base class such as UnifiedMap to augment. + * + *

Implementor should inherit from a Map-like class (primitive map possible) + * and bind the lowLevel* methods accordingly. + * + * @noreference This interface is not intended to be referenced by clients. + * @noimplement This interface is not intended to be implemented by clients. + * @author Gabor Bergmann + */ + public static interface ToMultisetsAbstract extends IMultiLookupAbstract> { + /** + * @return a fresh, empty marked multiset + */ + public MarkedMemory.MarkedMultiset createMarkedMultiset(); + + @Override + public default boolean negativesAllowed() { + return false; + } + @Override + default boolean duplicatesAllowed() { + return true; + } + + @Override + public default boolean addToBucket(MarkedMemory.MarkedMultiset bucket, Value value, boolean throwIfImpossible) { + return bucket.addOne(value); + } + + @Override + public default boolean removeFromBucket(MarkedMemory.MarkedMultiset bucket, Value value, boolean throwIfImpossible) { + return throwIfImpossible ? bucket.removeOne(value) : bucket.removeOneOrNop(value); + } + + @Override + public default Value asSingleton(MarkedMemory.MarkedMultiset bucket) { + if (bucket.size() != 1) return null; + Value candidate = bucket.iterator().next(); + return bucket.getCount(candidate) == 1 ? candidate : null; + } + + @Override + public default MarkedMemory.MarkedMultiset createSingletonBucket(Value value) { + MarkedMemory.MarkedMultiset result = createMarkedMultiset(); + result.addOne(value); + return result; + } + + @Override + public default MarkedMemory.MarkedMultiset createDeltaBucket(Value positive, Value negative) { + throw new IllegalStateException(); + } + } + + + // TODO add ToDeltaBagsAbstract + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java new file mode 100644 index 00000000..bdd5d597 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IMultiset.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +/** + * An {@link IMemory} that always contains values with a nonnegative multiplicity. + * + *

In case a write operation caused underflow, an {@link IllegalStateException} is thrown. + * + * @author Gabor Bergmann + * @since 1.7 + */ +public interface IMultiset extends IMemory { + + /** + * Adds the given number of occurrences to the memory. The count value must be a positive number. + * + * @param count + * the number of occurrences + * @return true if the tuple was not present before in the memory + */ + boolean addPositive(T value, int count); + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java new file mode 100644 index 00000000..cd25dc95 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/IProvider.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.function.Function; +import java.util.function.Supplier; + +import tools.refinery.viatra.runtime.matchers.psystem.queries.PQuery; + +/** + * A provider interface useful in various registry instances. + * + * @author Zoltan Ujhelyi + * + */ +public interface IProvider extends Supplier{ + + public final class ProvidedValueFunction implements Function, PQuery> { + @Override + public PQuery apply(IProvider input) { + return (input == null) ? null : input.get(); + } + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java new file mode 100644 index 00000000..0c03da48 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/ISetMemory.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.function.BiConsumer; + +/** + * An {@link IMemory} that always contains values with a 0 or +1 multiplicity. + * + *

In case a write operation causes underflow or overflow, an {@link IllegalStateException} is thrown. + * + * @author Gabor Bergmann + * @since 2.0 + */ +public interface ISetMemory extends IMemory { + + @Override + default void forEachEntryWithMultiplicities(BiConsumer entryConsumer) { + for (T t : this.distinctValues()) entryConsumer.accept(t, 1); + } + + + @Override + default boolean removeOne(T value) { + if (!removeOneOrNop(value)) + throw new IllegalStateException(); + return true; + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java new file mode 100644 index 00000000..3be078bd --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MapBackedMemoryView.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.function.BiConsumer; + +/** + * Wraps a Map (mapping elements to non-zero multiplicities) into an {@link IMemoryView}. + * + * @author Gabor Bergmann + * @since 2.0 + */ +public class MapBackedMemoryView implements IMemoryView { + + private Map wrapped; + + /** + * @param wrapped an equivalent map from contained objects to multiplicities + */ + protected MapBackedMemoryView(Map wrapped) { + super(); + this.wrapped = wrapped; + } + + @Override + public Iterator iterator() { + return wrapped.keySet().iterator(); + } + + @Override + public int getCount(T value) { + return getCountUnsafe(value); + } + + @Override + public int getCountUnsafe(Object value) { + Integer count = wrapped.get(value); + return count == null ? 0 : count; + } + + @Override + public boolean containsNonZero(T value) { + return wrapped.containsKey(value); + } + + @Override + public boolean containsNonZeroUnsafe(Object value) { + return wrapped.containsKey(value); + } + + @Override + public int size() { + return wrapped.size(); + } + + @Override + public boolean isEmpty() { + return wrapped.isEmpty(); + } + + @Override + public Set distinctValues() { + return wrapped.keySet(); + } + + + @Override + public void forEachEntryWithMultiplicities(BiConsumer entryConsumer) { + for (Entry entry : wrapped.entrySet()) { + entryConsumer.accept(entry.getKey(), entry.getValue()); + } + } + + @Override + public Iterable> entriesWithMultiplicities() { + return wrapped.entrySet(); + } + + @Override + public int hashCode() { + return IMemoryView.hashCode(this); + } + @Override + public boolean equals(Object obj) { + return IMemoryView.equals(this, obj); + } + + @Override + public String toString() { + return wrapped.toString(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java new file mode 100644 index 00000000..d22dcbe7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MarkedMemory.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +/** + * Internal marker type, must only be instantiated inside implementors of IMultiLookupImpl + * @noimplement This interface is not intended to be implemented by clients. + * @since 2.0 + */ +public interface MarkedMemory extends IMemory { + + static interface MarkedSet extends MarkedMemory {} + static interface MarkedMultiset extends MarkedMemory {} + static interface MarkedDeltaBag extends MarkedMemory {} +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java new file mode 100644 index 00000000..49711a89 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/MemoryViewBackedMapView.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * A partial and read-only Map implementation, mapping elements to multiplicities backed by an {@link IMemoryView}. + * + *

Not implemented: write methods. + * + *

Inefficiently implemented: {@link #containsValue(Object)}, {@link #values()}, {@link #entrySet()}. + * + * @author Gabor Bergmann + * @since 2.0 + */ +public class MemoryViewBackedMapView implements Map { + + private static final String READ_ONLY = "Read only"; + private final IMemoryView wrapped; + + /** + * @param wrapped a memory view whose contents are to be exposed as an element-to-integer map. + */ + protected MemoryViewBackedMapView(IMemoryView wrapped) { + super(); + this.wrapped = wrapped; + } + + @Override + public int size() { + return wrapped.size(); + } + + @Override + public boolean isEmpty() { + return wrapped.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return wrapped.containsNonZeroUnsafe(key); + } + + @Override + public boolean containsValue(Object value) { + if (value instanceof Integer) { + for (Entry entry : wrapped.entriesWithMultiplicities()) { + if (entry.getValue().equals(value)) return true; + } + } + return false; + } + + @Override + public Integer put(T key, Integer value) { + throw new UnsupportedOperationException(READ_ONLY); + } + + @Override + public Integer get(Object key) { + int count = wrapped.getCountUnsafe(key); + if (count == 0) return null; else return count; + } + + @Override + public Integer remove(Object key) { + throw new UnsupportedOperationException(READ_ONLY); + } + + @Override + public void putAll(Map m) { + throw new UnsupportedOperationException(READ_ONLY); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(READ_ONLY); + } + + @Override + public Set keySet() { + return wrapped.distinctValues(); + } + + @Override + public Collection values() { + Collection result = new ArrayList<>(); + wrapped.forEachEntryWithMultiplicities((value, count) -> result.add(count)); + return result; + } + + @Override + public Set> entrySet() { + Set> result = new HashSet<>(); + for (Entry entry : wrapped.entriesWithMultiplicities()) { + result.add(entry); + } + return result; + } + + + @Override + public String toString() { + return wrapped.toString(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java new file mode 100644 index 00000000..e9e5e3a0 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Preconditions.java @@ -0,0 +1,208 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.function.Supplier; + +/** + * This class was motivated by the similar Preconditions class from Guava to provide simple precondition checking + * functionality. However, as starting with version 2.0 the runtime of VIATRA Query should not depend on Guava, the + * relevant functionality of the Preconditions checking functionality will be implemented here. + * + * @author Zoltan Ujhelyi + * @since 2.0 + * + */ +public final class Preconditions { + + private Preconditions() { + /* Utility class constructor */ } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression + * a boolean expression + * @throws IllegalArgumentException + * if {@code expression} is false + */ + public static void checkArgument(boolean expression) { + if (!expression) { + throw new IllegalArgumentException(); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression + * a boolean expression + * @param errorMessage + * the exception message to use if the check fails + * @throws IllegalArgumentException + * if {@code expression} is false + */ + public static void checkArgument(boolean expression, String errorMessage) { + if (!expression) { + throw new IllegalArgumentException(errorMessage); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression + * a boolean expression + * @param errorMessageTemplate + * a template for the exception message should the check fail using the Java Formatter syntax; the same + * as used by {@link String#format(String, Object...)}. + * @param errorMessageArgs + * the arguments to be substituted into the message template. + * @throws IllegalArgumentException + * if {@code expression} is false + * @throws NullPointerException + * if the check fails and either {@code errorMessageTemplate} or {@code errorMessageArgs} is null (don't + * let this happen) + */ + public static void checkArgument(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { + if (!expression) { + throw new IllegalArgumentException(String.format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures the truth of an expression involving one or more parameters to the calling method. + * + * @param expression + * a boolean expression + * @param messageSupplier a supplier that is called to calculate the error message if necessary + * @throws IllegalArgumentException + * if {@code expression} is false + */ + public static void checkArgument(boolean expression, Supplier messageSupplier) { + if (!expression) { + throw new IllegalArgumentException(messageSupplier.get()); + } + } + + /** + * Ensures the truth of an expression involving one or more fields of a class. + * + * @param expression + * a boolean expression + * @throws IllegalStateException + * if {@code expression} is false + */ + public static void checkState(boolean expression) { + if (!expression) { + throw new IllegalStateException(); + } + } + + /** + * Ensures the truth of an expression involving one or more fields of a class. + * + * @param expression + * a boolean expression + * @param errorMessage + * the exception message to use if the check fails + * @throws IllegalStateException + * if {@code expression} is false + */ + public static void checkState(boolean expression, String errorMessage) { + if (!expression) { + throw new IllegalStateException(errorMessage); + } + } + + /** + * Ensures the truth of an expression involving one or more fields of a class. + * + * @param expression + * a boolean expression + * @param errorMessageTemplate + * a template for the exception message should the check fail using the Java Formatter syntax; the same + * as used by {@link String#format(String, Object...)}. + * @param errorMessageArgs + * the arguments to be substituted into the message template. + * @throws IllegalStateException + * if {@code expression} is false + * @throws NullPointerException + * if the check fails and either {@code errorMessageTemplate} or {@code errorMessageArgs} is null (don't + * let this happen) + */ + public static void checkState(boolean expression, String errorMessageTemplate, Object... errorMessageArgs) { + if (!expression) { + throw new IllegalStateException(String.format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures the truth of an expression involving one or more fields of a class. + * + * @param expression + * a boolean expression + * @param messageSupplier a supplier that is called to calculate the error message if necessary + * @throws IllegalStateException + * if {@code expression} is false + */ + public static void checkState(boolean expression, Supplier messageSupplier) { + if (!expression) { + throw new IllegalStateException(messageSupplier.get()); + } + } + + /** + * Ensures that an index is appropriate for a list or array of given size. + * + * @param index + * @param size + * @throws IndexOutOfBoundsException + * if index is negative or is greater or equal to size + */ + public static void checkElementIndex(int index, int size) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(); + } + } + + /** + * Ensures that an index is appropriate for a list or array of given size. + * + * @param index + * @param size + * @param errorMessageTemplate + * a template for the exception message should the check fail using the Java Formatter syntax; the same + * as used by {@link String#format(String, Object...)}. + * @param errorMessageArgs + * the arguments to be substituted into the message template. + * @throws IndexOutOfBoundsException + * if index is negative or is greater or equal to size + */ + public static void checkElementIndex(int index, int size, String errorMessageTemplate, Object... errorMessageArgs) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(String.format(errorMessageTemplate, errorMessageArgs)); + } + } + + /** + * Ensures that an index is appropriate for a list or array of given size. + * + * @param index + * @param size + * @param messageSupplier a supplier that is called to calculate the error message if necessary + * @throws IndexOutOfBoundsException + * if index is negative or is greater or equal to size + */ + public static void checkElementIndex(int index, int size, Supplier messageSupplier) { + if (index < 0 || index >= size) { + throw new IndexOutOfBoundsException(messageSupplier.get()); + } + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java new file mode 100644 index 00000000..c4e6b5af --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/PurgableCache.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2010-2017, Zoltan Ujhelyi, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +/** + * @author Zoltan Ujhelyi + * @since 1.7 + * @noreference This class is not intended to be referenced by clients. + */ +public class PurgableCache implements ICache { + + Map storage = new HashMap<>(); + + @Override + @SuppressWarnings("unchecked") + public T getValue(Object key, Class clazz, Supplier valueProvider) { + if (storage.containsKey(key)) { + Object value = storage.get(key); + Preconditions.checkState(clazz.isInstance(value), "Cache stores for key %s a value of %s that is incompatible with the requested type %s", key, value, clazz); + return (T) value; + } else { + T value = valueProvider.get(); + storage.put(key, value); + return value; + } + } + + /** + * Removes all values stored in the cache + */ + public void purge() { + storage.clear(); + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java new file mode 100644 index 00000000..3749fe06 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Sets.java @@ -0,0 +1,90 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Gabor Bergmann - initial API and implementation + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * This class was motivated by the similar Sets class from Guava to provide simple set manipulation + * functionality. However, as starting with version 2.3 the runtime of VIATRA Query should not depend on Guava, + * not even internally, the relevant subset of Sets methods will be reimplemented here. + * + *

The current approach is to delegate to Eclipse Collections wherever possible. + * Such glue methods are useful so that downstream clients can avoid directly depending on Eclipse Collections. + * + *

Without an equivalent from Eclipse Collections, {@link #cartesianProduct(List)} is implemented here from scratch. + * + * @author Gabor Bergmann + * @since 2.3 + */ +public final class Sets { + + /** + * @since 2.4 + */ + public static Set newSet(Iterable elements) { + return org.eclipse.collections.impl.factory.Sets.mutable.ofAll(elements); + } + + public static Set intersection(Set left, Set right) { + return org.eclipse.collections.impl.factory.Sets.intersect(left, right); + } + + public static Set difference(Set left, Set right) { + return org.eclipse.collections.impl.factory.Sets.difference(left, right); + } + + public static Set union(Set left, Set right) { + return org.eclipse.collections.impl.factory.Sets.union(left, right); + } + + public static Set> powerSet(Set set) { + return org.eclipse.collections.impl.factory.Sets.powerSet(set); + } + + public static Set> cartesianProduct(List> setsList) { + + class Suffix { // simple immutable linked list + private A head; + private Suffix next; + + public Suffix(A head, Suffix next) { + super(); + this.head = head; + this.next = next; + } + + public List toList() { + ArrayList result = new ArrayList<>(); + for (Suffix cursor = this; cursor!=null; cursor = cursor.next) + result.add(cursor.head); + return result; + } + } + + // build result lists from end to start, in the form of suffixes + Stream suffixes = Stream.of((Suffix) null /* empty suffix*/); + for (int i = setsList.size()-1; i>=0; --i) { // iterate sets in reverse order + Set set = setsList.get(i); + suffixes = suffixes.flatMap(suffix -> set.stream().map(newElement -> new Suffix(newElement, suffix))); + } + + + return suffixes.map(Suffix::toList).collect(Collectors.toSet()); + } + + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java new file mode 100644 index 00000000..8f8bc228 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/Signed.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Objects; + +/** + * A piece of data associated with a direction. + * + * @author Tamas Szabo + * @since 2.4 + */ +public class Signed> { + + private final Payload payload; + private final Direction direction; + + public Signed(final Direction direction, final Payload payload) { + this.payload = payload; + this.direction = direction; + } + + public Payload getPayload() { + return payload; + } + + public Direction getDirection() { + return direction; + } + + @Override + public int hashCode() { + return Objects.hash(direction, payload); + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } else if (obj == null || this.getClass() != obj.getClass()) { + return false; + } else { + @SuppressWarnings("rawtypes") + final Signed other = (Signed) obj; + return direction == other.direction && Objects.equals(payload, other.payload); + } + } + + @Override + public String toString() { + return this.direction.asSign() + this.payload.toString(); + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java new file mode 100644 index 00000000..cc5963f7 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonInstanceProvider.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2010-2015, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +/** + * A provider implementation that always returns the same object instance. + * @author Zoltan Ujhelyi + */ +public class SingletonInstanceProvider implements IProvider{ + + private T instance; + + public SingletonInstanceProvider(T instance) { + Preconditions.checkArgument(instance != null, "Instance parameter must not be null."); + this.instance = instance; + } + + @Override + public T get() { + return instance; + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java new file mode 100644 index 00000000..b303f9ad --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/SingletonMemoryView.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2010-2018, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Collections; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Set; + +/** + * An immutable memory view that consists of a single non-null element with multiplicity 1. + * @author Gabor Bergmann + * @since 2.0 + */ +public final class SingletonMemoryView implements IMemoryView { + + private Value wrapped; + private static final int ONE_HASH = Integer.valueOf(1).hashCode(); + + public SingletonMemoryView(Value value) { + this.wrapped = value; + } + + @Override + public Iterator iterator() { + return new Iterator() { + boolean hasNext = true; + + @Override + public boolean hasNext() { + return hasNext; + } + + @Override + public Value next() { + if (hasNext) { + hasNext = false; + return wrapped; + } else throw new NoSuchElementException(); + } + }; + } + + @Override + public int getCount(Value value) { + return wrapped.equals(value) ? 1 : 0; + } + + @Override + public int getCountUnsafe(Object value) { + return wrapped.equals(value) ? 1 : 0; + } + + @Override + public boolean containsNonZero(Value value) { + return wrapped.equals(value); + } + + @Override + public boolean containsNonZeroUnsafe(Object value) { + return wrapped.equals(value); + } + + @Override + public int size() { + return 1; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public Set distinctValues() { + return Collections.singleton(wrapped); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof IMemoryView) { + IMemoryView other = (IMemoryView) obj; + if (1 != other.size()) return false; + if (1 != other.getCountUnsafe(wrapped)) return false; + return true; + } + return false; + } + + @Override + public int hashCode() { + return wrapped.hashCode() ^ ONE_HASH; + } + + @Override + public String toString() { + return "{" + wrapped + "}"; + } +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java new file mode 100644 index 00000000..90fcad4d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/TimelyMemory.java @@ -0,0 +1,517 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util; + +import java.util.Collections; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.Set; +import java.util.TreeMap; + +import tools.refinery.viatra.runtime.matchers.tuple.ITuple; +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.resumable.Resumable; +import tools.refinery.viatra.runtime.matchers.util.resumable.UnmaskedResumable; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timeline; +import tools.refinery.viatra.runtime.matchers.util.timeline.Timelines; + +/** + * A timely memory implementation that incrementally maintains the {@link Timeline}s of tuples. The memory is capable of + * lazy folding (see {@link Resumable}). + * + * @author Tamas Szabo + * @since 2.3 + */ +public class TimelyMemory> implements Clearable, UnmaskedResumable { + + protected final Map> counters; + protected final Map> timelines; + public final TreeMap> foldingState; + protected final Set presentAtInfinity; + protected final boolean isLazy; + protected final Diff EMPTY_DIFF = new Diff(); + + public TimelyMemory() { + this(false); + } + + public TimelyMemory(final boolean isLazy) { + this.counters = CollectionsFactory.createMap(); + this.timelines = CollectionsFactory.createMap(); + this.presentAtInfinity = CollectionsFactory.createSet(); + this.isLazy = isLazy; + if (isLazy) { + this.foldingState = CollectionsFactory.createTreeMap(); + } else { + this.foldingState = null; + } + } + + @Override + public Set getResumableTuples() { + if (this.foldingState == null || this.foldingState.isEmpty()) { + return Collections.emptySet(); + } else { + return this.foldingState.firstEntry().getValue().keySet(); + } + } + + @Override + public Timestamp getResumableTimestamp() { + if (this.foldingState == null || this.foldingState.isEmpty()) { + return null; + } else { + return this.foldingState.firstKey(); + } + } + + /** + * Registers the given folding state for the specified timestamp and tuple. If there is already a state stored, the + * two states will be merged together. + */ + protected void addFoldingState(final Tuple tuple, final FoldingState state, final Timestamp timestamp) { + assert state.diff != 0; + final Map tupleMap = this.foldingState.computeIfAbsent(timestamp, + k -> CollectionsFactory.createMap()); + tupleMap.compute(tuple, (k, v) -> { + return v == null ? state : v.merge(state); + }); + } + + @Override + public Map> resumeAt(final Timestamp timestamp) { + Timestamp current = this.getResumableTimestamp(); + if (current == null) { + throw new IllegalStateException("There is othing to fold!"); + } else if (current.compareTo(timestamp) != 0) { + // It can happen that already registered folding states end up having zero diffs, + // and we are instructed to continue folding at a timestamp that is higher + // than the lowest timestamp with a folding state. + // However, we only do garbage collection in doFoldingState, so now it is time to + // first clean up those states with zero diffs. + while (current != null && current.compareTo(timestamp) < 0) { + final Map tupleMap = this.foldingState.remove(current); + for (final Entry entry : tupleMap.entrySet()) { + final Tuple key = entry.getKey(); + final FoldingState value = entry.getValue(); + if (value.diff != 0) { + throw new IllegalStateException("Expected zero diff during garbage collection at " + current + + ", but the diff was " + value.diff + "!"); + } + doFoldingStep(key, value, current); + } + current = this.getResumableTimestamp(); + } + if (current == null || current.compareTo(timestamp) != 0) { + throw new IllegalStateException("Expected to continue folding at " + timestamp + "!"); + } + } + + final Map> diffMap = CollectionsFactory.createMap(); + final Map tupleMap = this.foldingState.remove(timestamp); + for (final Entry entry : tupleMap.entrySet()) { + final Tuple key = entry.getKey(); + final FoldingState value = entry.getValue(); + diffMap.put(key, doFoldingStep(key, value, timestamp)); + } + + if (this.foldingState.get(timestamp) != null) { + throw new IllegalStateException( + "Folding at " + timestamp + " produced more folding work at the same timestamp!"); + } + + return diffMap; + } + + protected Diff doFoldingStep(final Tuple tuple, final FoldingState state, final Timestamp timestamp) { + final CumulativeCounter counter = getCounter(tuple, timestamp); + if (state.diff == 0) { + gcCounters(counter, tuple, timestamp); + return EMPTY_DIFF; + } else { + final Diff resultDiff = new Diff<>(); + final Timestamp nextTimestamp = this.counters.get(tuple).higherKey(timestamp); + + final int oldCumulative = counter.cumulative; + + counter.cumulative += state.diff; + + computeDiffsLazy(state.diff < 0 ? Direction.DELETE : Direction.INSERT, oldCumulative, counter.cumulative, + timestamp, nextTimestamp, resultDiff); + + gcCounters(counter, tuple, timestamp); + updateTimeline(tuple, resultDiff); + + // prepare folding state for next timestamp + if (nextTimestamp != null) { + // propagate the incoming diff, not the diff stored in counter + addFoldingState(tuple, new FoldingState(state.diff), nextTimestamp); + } + + return resultDiff; + } + } + + /** + * On-demand initializes and returns the counter for the given tuple and timestamp. + */ + protected CumulativeCounter getCounter(final Tuple tuple, final Timestamp timestamp) { + final TreeMap counterTimeline = this.counters.computeIfAbsent(tuple, + k -> CollectionsFactory.createTreeMap()); + + final CumulativeCounter counter = counterTimeline.computeIfAbsent(timestamp, k -> { + final Entry previousCounter = counterTimeline.lowerEntry(k); + final int previousCumulative = previousCounter == null ? 0 : previousCounter.getValue().cumulative; + return new CumulativeCounter(0, previousCumulative); + }); + + return counter; + } + + /** + * Garbage collects the counter of the given tuple and timestamp if the new diff is zero. + */ + protected void gcCounters(final CumulativeCounter counter, final Tuple tuple, final Timestamp timestamp) { + if (counter.diff == 0) { + final TreeMap counterMap = this.counters.get(tuple); + counterMap.remove(timestamp); + if (counterMap.isEmpty()) { + this.counters.remove(tuple); + } + } + } + + /** + * Utility method that computes the timeline diffs in case of lazy memories. The diffs will be inserted into the + * input parameter. This method computes diffs for entire plateaus that spans from timestamp to nextTimestamp. + * + * Compared to the eager version of this method, the lazy version makes use of both the old and the new cumulative + * values because it can happen that the cumulative is incremented by a value that is larger than 1 (as folding + * states are merged together). This means that we cant decide whether the cumulative became positive by comparing + * the new value to 1. + */ + protected void computeDiffsLazy(final Direction direction, final int oldCumulative, final int newCumulative, + final Timestamp timestamp, final Timestamp nextTimestamp, final Diff diffs) { + if (direction == Direction.INSERT) { + if (newCumulative == 0) { + throw new IllegalStateException("Cumulative count can never be negative!"); + } else { + if (oldCumulative == 0 /* current became positive */) { + // (1) either we sent out a DELETE before and now we need to cancel it, + // (2) or we just INSERT this for the first time + diffs.add(new Signed<>(Direction.INSERT, timestamp)); + if (nextTimestamp != null) { + diffs.add(new Signed<>(Direction.DELETE, nextTimestamp)); + } + } else /* current stays positive */ { + // nothing to do + } + } + } else { + if (newCumulative < 0) { + throw new IllegalStateException("Cumulative count can never be negative!"); + } else { + if (newCumulative == 0 /* current became zero */) { + diffs.add(new Signed<>(Direction.DELETE, timestamp)); + if (nextTimestamp != null) { + diffs.add(new Signed<>(Direction.INSERT, nextTimestamp)); + } + } else /* current stays positive */ { + // nothing to do + } + } + } + } + + /** + * Utility method that computes the timeline diffs in case of eager memories. The diffs will be inserted into the + * input parameter. This method computes diffs that describe momentary changes instead of plateaus. Returns a + * {@link SignChange} that describes how the sign has changed at the given timestamp. + */ + protected SignChange computeDiffsEager(final Direction direction, final CumulativeCounter counter, + final SignChange signChangeAtPrevious, final Timestamp timestamp, final Diff diffs) { + if (direction == Direction.INSERT) { + if (counter.cumulative == 0) { + throw new IllegalStateException("Cumulative count can never be negative!"); + } else { + if (counter.cumulative == 1 /* current became positive */) { + if (signChangeAtPrevious != SignChange.BECAME_POSITIVE) { + // (1) either we sent out a DELETE before and now we need to cancel it, + // (2) or we just INSERT this for the first time + diffs.add(new Signed<>(Direction.INSERT, timestamp)); + } else { + // we have already emitted this at the previous timestamp + // both previous and current became positive + throw new IllegalStateException( + "This would mean that the diff at current is 0 " + counter.diff); + } + + // remember for next timestamp + return SignChange.BECAME_POSITIVE; + } else /* current stays positive */ { + if (signChangeAtPrevious == SignChange.BECAME_POSITIVE) { + // we sent out an INSERT before and now the timeline is positive already starting at previous + // we need to cancel the effect of this with a DELETE + diffs.add(new Signed<>(Direction.DELETE, timestamp)); + } else { + // this is normal, both previous and current was positive and stays positive + } + + // remember for next timestamp + return SignChange.IRRELEVANT; + } + } + } else { + if (counter.cumulative < 0) { + throw new IllegalStateException("Cumulative count can never be negative!"); + } else { + if (counter.cumulative == 0 /* current became zero */) { + if (signChangeAtPrevious != SignChange.BECAME_ZERO) { + // (1) either we sent out a INSERT before and now we need to cancel it, + // (2) or we just DELETE this for the first time + diffs.add(new Signed<>(Direction.DELETE, timestamp)); + } else { + // we have already emitted this at the previous timestamp + // both previous and current became zero + throw new IllegalStateException( + "This would mean that the diff at current is 0 " + counter.diff); + } + + // remember for next timestamp + return SignChange.BECAME_ZERO; + } else /* current stays positive */ { + if (signChangeAtPrevious == SignChange.BECAME_ZERO) { + // we sent out a DELETE before and now the timeline is zero already starting at previous + // we need to cancel the effect of this with a INSERT + diffs.add(new Signed<>(Direction.INSERT, timestamp)); + } else { + // this is normal, both previous and current was positive and stays positive + } + + // remember for next timestamp + return SignChange.IRRELEVANT; + } + } + } + } + + public Diff put(final Tuple tuple, final Timestamp timestamp) { + if (this.isLazy) { + return putLazy(tuple, timestamp); + } else { + return putEager(tuple, timestamp); + } + } + + public Diff remove(final Tuple tuple, final Timestamp timestamp) { + if (this.isLazy) { + return removeLazy(tuple, timestamp); + } else { + return removeEager(tuple, timestamp); + } + } + + protected Diff putEager(final Tuple tuple, final Timestamp timestamp) { + final Diff resultDiff = new Diff<>(); + final CumulativeCounter counter = getCounter(tuple, timestamp); + ++counter.diff; + + // before the INSERT timestamp, no change at all + // it cannot happen that those became positive in this round + SignChange signChangeAtPrevious = SignChange.IRRELEVANT; + + final NavigableMap nextCounters = this.counters.get(tuple).tailMap(timestamp, + true); + for (final Entry currentEntry : nextCounters.entrySet()) { + final Timestamp currentTimestamp = currentEntry.getKey(); + final CumulativeCounter currentCounter = currentEntry.getValue(); + ++currentCounter.cumulative; + signChangeAtPrevious = computeDiffsEager(Direction.INSERT, currentCounter, signChangeAtPrevious, + currentTimestamp, resultDiff); + } + + gcCounters(counter, tuple, timestamp); + updateTimeline(tuple, resultDiff); + + return resultDiff; + } + + protected Diff putLazy(final Tuple tuple, final Timestamp timestamp) { + final CumulativeCounter counter = getCounter(tuple, timestamp); + counter.diff += 1; + // before the INSERT timestamp, no change at all + // it cannot happen that those became positive in this round + addFoldingState(tuple, new FoldingState(+1), timestamp); + return EMPTY_DIFF; + } + + protected Diff removeEager(final Tuple tuple, final Timestamp timestamp) { + final Diff resultDiff = new Diff<>(); + final CumulativeCounter counter = getCounter(tuple, timestamp); + --counter.diff; + + // before the DELETE timestamp, no change at all + // it cannot happen that those became zero in this round + SignChange signChangeAtPrevious = SignChange.IRRELEVANT; + + final NavigableMap nextCounters = this.counters.get(tuple).tailMap(timestamp, + true); + for (final Entry currentEntry : nextCounters.entrySet()) { + final Timestamp currentTimestamp = currentEntry.getKey(); + final CumulativeCounter currentCounter = currentEntry.getValue(); + --currentCounter.cumulative; + signChangeAtPrevious = computeDiffsEager(Direction.DELETE, currentCounter, signChangeAtPrevious, + currentTimestamp, resultDiff); + } + + gcCounters(counter, tuple, timestamp); + updateTimeline(tuple, resultDiff); + + return resultDiff; + } + + protected Diff removeLazy(final Tuple tuple, final Timestamp timestamp) { + final CumulativeCounter counter = getCounter(tuple, timestamp); + counter.diff -= 1; + // before the DELETE timestamp, no change at all + // it cannot happen that those became zero in this round + addFoldingState(tuple, new FoldingState(-1), timestamp); + return EMPTY_DIFF; + } + + /** + * Updates and garbage collects the timeline of the given tuple based on the given timeline diff. + */ + protected void updateTimeline(final Tuple tuple, final Diff diff) { + if (!diff.isEmpty()) { + this.timelines.compute(tuple, (k, oldTimeline) -> { + this.presentAtInfinity.remove(tuple); + final Timeline timeline = oldTimeline == null ? Timelines.createFrom(diff) + : oldTimeline.mergeAdditive(diff); + if (timeline.isPresentAtInfinity()) { + this.presentAtInfinity.add(tuple); + } + if (timeline.isEmpty()) { + return null; + } else { + return timeline; + } + }); + } + } + + /** + * @since 2.8 + */ + public Set getTuplesAtInfinity() { + return this.presentAtInfinity; + } + + /** + * Returns the number of tuples that are present at the moment 'infinity'. + */ + public int getCountAtInfinity() { + return this.presentAtInfinity.size(); + } + + /** + * Returns true if the given tuple is present at the moment 'infinity'. + */ + public boolean isPresentAtInfinity(final Tuple tuple) { + final Timeline timeline = this.timelines.get(tuple); + if (timeline == null) { + return false; + } else { + return timeline.isPresentAtInfinity(); + } + } + + public boolean isEmpty() { + return this.counters.isEmpty(); + } + + public int size() { + return this.counters.size(); + } + + public Set keySet() { + return this.counters.keySet(); + } + + public Map> asMap() { + return this.timelines; + } + + public Timeline get(final ITuple tuple) { + return this.timelines.get(tuple); + } + + @Override + public void clear() { + this.counters.clear(); + this.timelines.clear(); + if (this.foldingState != null) { + this.foldingState.clear(); + } + } + + public boolean containsKey(final ITuple tuple) { + return this.counters.containsKey(tuple); + } + + @Override + public String toString() { + return this.counters + "\n" + this.timelines + "\n" + this.foldingState + "\n"; + } + + protected static final class CumulativeCounter { + protected int diff; + protected int cumulative; + + protected CumulativeCounter(final int diff, final int cumulative) { + this.diff = diff; + this.cumulative = cumulative; + } + + @Override + public String toString() { + return "{diff=" + this.diff + ", cumulative=" + this.cumulative + "}"; + } + + } + + protected static final class FoldingState { + protected final int diff; + + protected FoldingState(final int diff) { + this.diff = diff; + } + + @Override + public String toString() { + return "{diff=" + this.diff + "}"; + } + + /** + * The returned result will never be null, even if the resulting diff is zero. + */ + public FoldingState merge(final FoldingState that) { + Preconditions.checkArgument(that != null); + return new FoldingState(this.diff + that.diff); + } + + } + + protected enum SignChange { + BECAME_POSITIVE, BECAME_ZERO, IRRELEVANT; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java new file mode 100644 index 00000000..ea70e61d --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/MaskedResumable.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util.resumable; + +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; + +/** + * A masked {@link Resumable} implementation, which maintains lazy folding per tuple signature. + * + * @author Tamas Szabo + * @since 2.4 + */ +public interface MaskedResumable> extends Resumable { + + /** + * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to + * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more + * folding to do towards higher timestamps. + */ + public Map>> resumeAt(final Timestamp timestamp); + + /** + * Returns the set of signatures for which lazy folding shall be resumed at the next timestamp. + */ + public Iterable getResumableSignatures(); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java new file mode 100644 index 00000000..2861df20 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/Resumable.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util.resumable; + +/** + * A resumable lazily folds its state towards higher timestamps. Folding shall be done in the increasing order of + * timestamps, and it shall be interrupted after each step. The resumable can then be instructed to resume the folding, + * one step at a time. + * + * @author Tamas Szabo + * @since 2.4 + */ +public interface Resumable> { + + /** + * Returns the smallest timestamp where lazy folding shall be resumed, or null if there is no more folding to do in this + * resumable. + */ + public Timestamp getResumableTimestamp(); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java new file mode 100644 index 00000000..1671940b --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/resumable/UnmaskedResumable.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util.resumable; + +import java.util.Map; + +import tools.refinery.viatra.runtime.matchers.tuple.Tuple; +import tools.refinery.viatra.runtime.matchers.util.timeline.Diff; + +/** + * A unmasked {@link Resumable} implementation, which maintains lazy folding without caring about tuple signatures. + * + * @author Tamas Szabo + * @since 2.4 + */ +public interface UnmaskedResumable> extends Resumable { + + /** + * When called, the folding of the state shall be resumed at the given timestamp. The resumable is expected to + * do a folding step at the given timestamp only. Afterwards, folding shall be interrupted, even if there is more + * folding to do towards higher timestamps. + */ + public Map> resumeAt(final Timestamp timestamp); + + /** + * Returns the set of tuples for which lazy folding shall be resumed at the next timestamp. + */ + public Iterable getResumableTuples(); + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java new file mode 100644 index 00000000..0532d094 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/CompactTimeline.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util.timeline; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; + +/** + * A compact timeline may cosist of an arbitrary amount of moments. + * It is backed by an {@link ArrayList}. + * + * @author Tamas Szabo + * @since 2.4 + */ +public class CompactTimeline> extends Timeline { + + protected final List elements; + + CompactTimeline() { + this.elements = new ArrayList<>(); + } + + CompactTimeline(final Timestamp timestamp) { + this(); + this.elements.add(timestamp); + } + + CompactTimeline(final List timestamps) { + this.elements = new ArrayList<>(timestamps.size()); + this.elements.addAll(timestamps); + } + + CompactTimeline(final Diff diff) { + this.elements = new ArrayList<>(diff.size()); + Direction expected = Direction.INSERT; + for (Signed signed : diff) { + if (!expected.equals(signed.getDirection())) { + throw new IllegalStateException(String.format("Expected direction (%s) constraint violated! %s @%s", + expected, diff, signed.getPayload())); + } + this.elements.add(signed.getPayload()); + expected = expected.opposite(); + } + } + + @Override + public Signed getSigned(final int index) { + final Direction direction = index % 2 == 0 ? Direction.INSERT : Direction.DELETE; + return new Signed<>(direction, this.getUnsigned(index)); + } + + @Override + public Timestamp getUnsigned(final int index) { + if (this.elements.size() <= index) { + throw new IllegalArgumentException( + "Timeline size (" + this.size() + ") is smaller than requested index " + index + "!"); + } else { + return this.elements.get(index); + } + } + + @Override + public int size() { + return this.elements.size(); + } + + @Override + public boolean isPresentAtInfinity() { + // if it has an odd length, then it ends with "INSERT" + return this.size() % 2 == 1; + } + + @Override + public Iterable> asChangeSequence() { + Iterable outer = this.elements; + return () -> { + final Iterator itr = outer.iterator(); + return new Iterator>() { + Direction direction = Direction.INSERT; + + @Override + public boolean hasNext() { + return itr.hasNext(); + } + + @Override + public Signed next() { + final Signed result = new Signed(direction, itr.next()); + direction = direction.opposite(); + return result; + } + }; + }; + } + + @Override + public boolean isEmpty() { + return this.elements.isEmpty(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java new file mode 100644 index 00000000..cec6049e --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Diff.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util.timeline; + +import java.util.ArrayList; + +import tools.refinery.viatra.runtime.matchers.util.Signed; + +/** + * The description of a delta that specifies how a {@link Timeline} changes. It consists of {@link Signed} timestamps that + * depict the moments of insertions and deletions on the timeline. + * + * @author Tamas Szabo + * @since 2.4 + * @param + * the type representing the timestamps + */ +public class Diff> extends ArrayList> { + + private static final long serialVersionUID = 3853460426655994160L; + + public Diff() { + + } + + public void appendWithCancellation(Signed item) { + if (this.isEmpty()) { + this.add(item); + } else { + final Signed last = this.get(this.size() - 1); + final int lastMinusItem = last.getPayload().compareTo(item.getPayload()); + if (lastMinusItem == 0) { + if (last.getDirection() != item.getDirection()) { + // cancellation + this.remove(this.size() - 1); + } else { + throw new IllegalStateException( + "Trying to insert or delete for the second time at the same timestamp! " + item); + } + } else if (lastMinusItem > 0) { + throw new IllegalStateException( + "Trying to append a timestamp that is smaller than the last one! " + last + " " + item); + } else { + this.add(item); + } + } + } + +} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java new file mode 100644 index 00000000..526a95f5 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/SingletonTimeline.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util.timeline; + +import java.util.Collections; + +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; + +/** + * A timeline which solely consists of one timestamp value, representing a single insertion. Intuitively, a singleton + * timeline always represents a bump which starts at the given timestamp and lasts till plus infinity. + * + * @author Tamas Szabo + * @since 2.4 + */ +public class SingletonTimeline> extends Timeline { + + protected final Timestamp start; + + SingletonTimeline(final Timestamp timestamp) { + this.start = timestamp; + } + + SingletonTimeline(final Diff diff) { + if (diff.size() != 1 || diff.get(0).getDirection() == Direction.DELETE) { + throw new IllegalArgumentException("There is only a single (insert) timestamp in the singleton timestamp!"); + } else { + this.start = diff.get(0).getPayload(); + } + } + + @Override + public Signed getSigned(final int index) { + return new Signed<>(Direction.INSERT, this.getUnsigned(index)); + } + + @Override + public Timestamp getUnsigned(final int index) { + if (index != 0) { + throw new IllegalArgumentException("There is only a single (insert) timestamp in the singleton timestamp!"); + } else { + return this.start; + } + } + + @Override + public int size() { + return 1; + } + + @Override + public boolean isPresentAtInfinity() { + return true; + } + + @Override + public Iterable> asChangeSequence() { + return Collections.singletonList(this.getSigned(0)); + } + + @Override + public boolean isEmpty() { + return false; + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java new file mode 100644 index 00000000..9214536c --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timeline.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util.timeline; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.Signed; + +/** + * A timeline describes the life cycle of a piece of data (typically a tuple in a relation) as a sequence of moments. + * Even moments represent appearances, odd moments represent disappearances. A timeline is immutable, once created, it + * is not possible to extend it with further moments. + * + * @author Tamas Szabo + * @since 2.4 + */ +public abstract class Timeline> { + + public abstract Iterable> asChangeSequence(); + + public abstract boolean isPresentAtInfinity(); + + public abstract boolean isEmpty(); + + public abstract int size(); + + public abstract Signed getSigned(final int index); + + public abstract Timestamp getUnsigned(final int index); + + public Timeline mergeMultiplicative(final Timeline that) { + final List result = new ArrayList<>(); + int thisIdx = 0, thatIdx = 0; + Timestamp thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; + Timestamp thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; + + while (thisNext != null || thatNext != null) { + int thisMinusThat = 0; + if (thisNext != null && thatNext != null) { + thisMinusThat = thisNext.compareTo(thatNext); + } + if (thisNext == null || thisMinusThat > 0) { + if (thisIdx % 2 == 1) { + result.add(thatNext); + } + thatIdx++; + thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; + } else if (thatNext == null || thisMinusThat < 0) { + if (thatIdx % 2 == 1) { + result.add(thisNext); + } + thisIdx++; + thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; + } else { + if (thisIdx % 2 == thatIdx % 2) { + result.add(thisNext); + } + thisIdx++; + thatIdx++; + thatNext = thatIdx < that.size() ? that.getUnsigned(thatIdx) : null; + thisNext = thisIdx < this.size() ? this.getUnsigned(thisIdx) : null; + } + } + + return Timelines.createFrom(result); + } + + /** + * Merges this timeline with the given timestamp diff. The expectation is that the resulting timeline starts with an + * insertion. The logic is similar to a merge sort; we iterate side-by-side over the timeline and the diff. During + * the merge, cancellation can happen if at the same timestamp we observe different signs at the corresponding + * timeline and diff elements. + */ + public Timeline mergeAdditive(final Diff diff) { + final Iterator> thisItr = this.asChangeSequence().iterator(); + final Iterator> diffItr = diff.iterator(); + final List result = new ArrayList<>(); + Direction expected = Direction.INSERT; + Signed thisNext = thisItr.hasNext() ? thisItr.next() : null; + Signed diffNext = diffItr.hasNext() ? diffItr.next() : null; + + while (thisNext != null || diffNext != null) { + int thisMinusDiff = 0; + if (thisNext != null && diffNext != null) { + thisMinusDiff = thisNext.getPayload().compareTo(diffNext.getPayload()); + } + + if (thisNext == null || thisMinusDiff > 0) { + if (!expected.equals(diffNext.getDirection())) { + throw new IllegalStateException( + String.format("Expected direction (%s) constraint violated! %s %s @%s", expected, this, + diff, diffNext.getPayload())); + } + result.add(diffNext.getPayload()); + diffNext = diffItr.hasNext() ? diffItr.next() : null; + expected = expected.opposite(); + } else if (diffNext == null || thisMinusDiff < 0) { + if (!expected.equals(thisNext.getDirection())) { + throw new IllegalStateException( + String.format("Expected direction (%s) constraint violated! %s %s @%s", expected, this, + diff, thisNext.getPayload())); + } + result.add(thisNext.getPayload()); + thisNext = thisItr.hasNext() ? thisItr.next() : null; + expected = expected.opposite(); + } else { + // they cancel out each other + if (diffNext.getDirection().equals(thisNext.getDirection())) { + throw new IllegalStateException(String.format("Changes do not cancel out each other! %s %s @%s", + this, diff, thisNext.getPayload())); + } + diffNext = diffItr.hasNext() ? diffItr.next() : null; + thisNext = thisItr.hasNext() ? thisItr.next() : null; + } + } + + return Timelines.createFrom(result); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("["); + boolean first = true; + for (final Signed element : this.asChangeSequence()) { + if (first) { + first = false; + } else { + builder.append(", "); + } + builder.append(element.toString()); + } + builder.append("]"); + return builder.toString(); + } + +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java new file mode 100644 index 00000000..747fda15 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/util/timeline/Timelines.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, itemis AG, Gabor Bergmann, IncQuery Labs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.matchers.util.timeline; + +import java.util.List; + +/** + * Utility class for creating {@link Timeline}s. + * @author Tamas Szabo + * @since 2.4 + */ +public final class Timelines> { + + public static > Timeline createEmpty() { + return new CompactTimeline(); + } + + public static > Timeline createFrom( + final Diff diffs) { + if (diffs.size() == 1) { + return new SingletonTimeline(diffs); + } else { + return new CompactTimeline(diffs); + } + } + + public static > Timeline createFrom( + final List timestamps) { + if (timestamps.size() == 1) { + return new SingletonTimeline(timestamps.get(0)); + } else { + return new CompactTimeline(timestamps); + } + } + + public static > Timeline createFrom(final Timestamp timestamp) { + return new SingletonTimeline(timestamp); + } + +} -- cgit v1.2.3-54-g00ecf From 163371a761d1a78f36ad33932581573d6de12c30 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sat, 19 Aug 2023 03:20:11 +0200 Subject: chore: mark modified VIATRA files Make sure the copyright headers are up to date. --- .../runtime/rete/construction/plancompiler/ReteRecipeCompiler.java | 1 + .../tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java | 1 + .../tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java | 1 + .../java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java | 1 + .../main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java | 1 + .../refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java | 1 + .../refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java | 1 + 7 files changed, 7 insertions(+) (limited to 'subprojects/viatra-runtime-rete') diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java index 6dd270bd..5df3a971 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/construction/plancompiler/ReteRecipeCompiler.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java index 1f380b45..38fe7c2f 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/matcher/RetePatternMatcher.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java index 6606a75d..b261d19d 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ConnectionFactory.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java index a40a8b7f..231bdfe5 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2010-2014, Bergmann Gabor, Istvan Rath and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java index 58285845..4c603a47 100644 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/api/ViatraQueryEngine.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java index a3a9d073..47a51629 100644 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/internal/apiimpl/ViatraQueryEngineImpl.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2004-2010 Gabor Bergmann and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java index e66c4eea..10ab19c8 100644 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/psystem/rewriters/PBodyCopier.java @@ -1,5 +1,6 @@ /******************************************************************************* * Copyright (c) 2010-2014, Marton Bur, Akos Horvath, Zoltan Ujhelyi, Istvan Rath and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. -- cgit v1.2.3-54-g00ecf From 6e11ebab0aa0dca1de5475761978073dd849aa36 Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sat, 19 Aug 2023 03:56:39 +0200 Subject: refactor: move ITC algorithms Since only RETE uses ITC, we may move ITC into the RETE project. Also removes unused ITC algorithms. --- .../rete/index/TransitiveClosureNodeIndexer.java | 14 +- .../runtime/rete/itc/alg/counting/CountingAlg.java | 226 ++++++++ .../rete/itc/alg/counting/CountingTcRelation.java | 259 +++++++++ .../rete/itc/alg/incscc/CountingListener.java | 36 ++ .../runtime/rete/itc/alg/incscc/IncSCCAlg.java | 609 +++++++++++++++++++ .../runtime/rete/itc/alg/misc/DFSPathFinder.java | 146 +++++ .../viatra/runtime/rete/itc/alg/misc/Edge.java | 38 ++ .../runtime/rete/itc/alg/misc/GraphHelper.java | 169 ++++++ .../rete/itc/alg/misc/IGraphPathFinder.java | 67 +++ .../runtime/rete/itc/alg/misc/ITcRelation.java | 31 + .../viatra/runtime/rete/itc/alg/misc/Tuple.java | 60 ++ .../viatra/runtime/rete/itc/alg/misc/bfs/BFS.java | 151 +++++ .../runtime/rete/itc/alg/misc/scc/PKAlg.java | 179 ++++++ .../viatra/runtime/rete/itc/alg/misc/scc/SCC.java | 146 +++++ .../runtime/rete/itc/alg/misc/scc/SCCProperty.java | 37 ++ .../runtime/rete/itc/alg/misc/scc/SCCResult.java | 81 +++ .../itc/alg/misc/topsort/TopologicalSorting.java | 73 +++ .../RepresentativeElectionAlgorithm.java | 140 +++++ .../alg/representative/RepresentativeObserver.java | 12 + .../StronglyConnectedComponentAlgorithm.java | 66 +++ .../WeaklyConnectedComponentAlgorithm.java | 85 +++ .../rete/itc/alg/util/CollectionHelper.java | 64 ++ .../runtime/rete/itc/graphimpl/DotGenerator.java | 160 +++++ .../viatra/runtime/rete/itc/graphimpl/Graph.java | 185 ++++++ .../itc/igraph/IBiDirectionalGraphDataSource.java | 37 ++ .../rete/itc/igraph/IBiDirectionalWrapper.java | 110 ++++ .../runtime/rete/itc/igraph/IGraphDataSource.java | 70 +++ .../runtime/rete/itc/igraph/IGraphObserver.java | 55 ++ .../runtime/rete/itc/igraph/ITcDataSource.java | 82 +++ .../runtime/rete/itc/igraph/ITcObserver.java | 39 ++ .../viatra/runtime/rete/network/NodeFactory.java | 6 +- .../communication/CommunicationTracker.java | 20 +- .../timely/TimelyCommunicationTracker.java | 8 +- .../rete/single/RepresentativeElectionNode.java | 6 +- .../runtime/rete/single/TransitiveClosureNode.java | 10 +- .../runtime/base/itc/alg/counting/CountingAlg.java | 226 -------- .../base/itc/alg/counting/CountingTcRelation.java | 259 --------- .../viatra/runtime/base/itc/alg/dred/DRedAlg.java | 308 ---------- .../runtime/base/itc/alg/dred/DRedTcRelation.java | 223 ------- .../runtime/base/itc/alg/fw/FloydWarshallAlg.java | 110 ---- .../base/itc/alg/incscc/CountingListener.java | 36 -- .../runtime/base/itc/alg/incscc/IncSCCAlg.java | 645 --------------------- .../runtime/base/itc/alg/misc/DFSPathFinder.java | 146 ----- .../viatra/runtime/base/itc/alg/misc/Edge.java | 38 -- .../runtime/base/itc/alg/misc/GraphHelper.java | 173 ------ .../base/itc/alg/misc/IGraphPathFinder.java | 67 --- .../runtime/base/itc/alg/misc/ITcRelation.java | 31 - .../viatra/runtime/base/itc/alg/misc/Tuple.java | 60 -- .../viatra/runtime/base/itc/alg/misc/bfs/BFS.java | 151 ----- .../runtime/base/itc/alg/misc/dfs/DFSAlg.java | 93 --- .../runtime/base/itc/alg/misc/scc/PKAlg.java | 179 ------ .../viatra/runtime/base/itc/alg/misc/scc/SCC.java | 146 ----- .../runtime/base/itc/alg/misc/scc/SCCProperty.java | 37 -- .../runtime/base/itc/alg/misc/scc/SCCResult.java | 81 --- .../itc/alg/misc/topsort/TopologicalSorting.java | 77 --- .../RepresentativeElectionAlgorithm.java | 140 ----- .../alg/representative/RepresentativeObserver.java | 12 - .../StronglyConnectedComponentAlgorithm.java | 66 --- .../WeaklyConnectedComponentAlgorithm.java | 85 --- .../base/itc/alg/util/CollectionHelper.java | 64 -- .../runtime/base/itc/graphimpl/DotGenerator.java | 160 ----- .../viatra/runtime/base/itc/graphimpl/Graph.java | 185 ------ .../itc/igraph/IBiDirectionalGraphDataSource.java | 37 -- .../base/itc/igraph/IBiDirectionalWrapper.java | 110 ---- .../runtime/base/itc/igraph/IGraphDataSource.java | 70 --- .../runtime/base/itc/igraph/IGraphObserver.java | 55 -- .../runtime/base/itc/igraph/ITcDataSource.java | 82 --- .../runtime/base/itc/igraph/ITcObserver.java | 39 -- 68 files changed, 3445 insertions(+), 4223 deletions(-) create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingAlg.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingTcRelation.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/CountingListener.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/IncSCCAlg.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/DFSPathFinder.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Edge.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/GraphHelper.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/IGraphPathFinder.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/ITcRelation.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Tuple.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/PKAlg.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCProperty.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCResult.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeObserver.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/util/CollectionHelper.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/DotGenerator.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/Graph.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalGraphDataSource.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalWrapper.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphDataSource.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphObserver.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcDataSource.java create mode 100644 subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcObserver.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java delete mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java (limited to 'subprojects/viatra-runtime-rete') diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java index b0bea8cb..77202004 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/index/TransitiveClosureNodeIndexer.java @@ -3,7 +3,7 @@ * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. - * + * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package tools.refinery.viatra.runtime.rete.index; @@ -13,7 +13,7 @@ import java.util.Collections; import java.util.Iterator; import java.util.Set; -import tools.refinery.viatra.runtime.base.itc.alg.incscc.IncSCCAlg; +import tools.refinery.viatra.runtime.rete.itc.alg.incscc.IncSCCAlg; import tools.refinery.viatra.runtime.matchers.tuple.MaskedTuple; import tools.refinery.viatra.runtime.matchers.tuple.Tuple; import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; @@ -86,21 +86,21 @@ public class TransitiveClosureNodeIndexer extends StandardIndexer implements Ite public int getBucketCount() { throw new UnsupportedOperationException(); } - + @Override public Collection getSignatures() { return asTupleCollection(tcAlg.getTcRelation()); } - + @Override public Iterator iterator() { return asTupleCollection(tcAlg.getTcRelation()).iterator(); } private Collection asTupleCollection( - Collection> tuples) { + Collection> tuples) { Set retSet = CollectionsFactory.createSet(); - for (tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple tuple : tuples) { + for (tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple tuple : tuples) { retSet.add(Tuples.staticArityFlatTupleOf(tuple.getSource(), tuple.getTarget())); } return retSet; @@ -117,5 +117,5 @@ public class TransitiveClosureNodeIndexer extends StandardIndexer implements Ite public Receiver getActiveNode() { return tcNode; } - + } diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingAlg.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingAlg.java new file mode 100644 index 00000000..199b44b1 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingAlg.java @@ -0,0 +1,226 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.counting; + +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.rete.itc.alg.misc.DFSPathFinder; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.IGraphPathFinder; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.ITcRelation; +import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalWrapper; +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphObserver; +import tools.refinery.viatra.runtime.rete.itc.igraph.ITcDataSource; +import tools.refinery.viatra.runtime.rete.itc.igraph.ITcObserver; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; + +/** + * This class is the optimized implementation of the Counting algorithm. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the nodes in the graph data source + */ +public class CountingAlg implements IGraphObserver, ITcDataSource { + + private CountingTcRelation tc = null; + private IBiDirectionalGraphDataSource gds = null; + private List> observers; + + /** + * Constructs a new Counting algorithm and initializes the transitive closure relation with the given graph data + * source. Attach itself on the graph data source as an observer. + * + * @param gds + * the graph data source instance + */ + public CountingAlg(IGraphDataSource gds) { + + if (gds instanceof IBiDirectionalGraphDataSource) { + this.gds = (IBiDirectionalGraphDataSource) gds; + } else { + this.gds = new IBiDirectionalWrapper(gds); + } + + observers = CollectionsFactory.>createObserverList(); + tc = new CountingTcRelation(true); + + initTc(); + gds.attachObserver(this); + } + + /** + * Initializes the transitive closure relation. + */ + private void initTc() { + this.setTcRelation(CountingTcRelation.createFrom(gds)); + } + + @Override + public void edgeInserted(V source, V target) { + if (!source.equals(target)) { + deriveTc(source, target, true); + } + } + + @Override + public void edgeDeleted(V source, V target) { + if (!source.equals(target)) { + deriveTc(source, target, false); + } + } + + @Override + public void nodeInserted(V n) { + + } + + @Override + public void nodeDeleted(V n) { + this.tc.deleteTupleEnd(n); + } + + /** + * Derives the transitive closure relation when an edge is inserted or deleted. + * + * @param source + * the source of the edge + * @param target + * the target of the edge + * @param dCount + * the value is -1 if an edge was deleted and +1 if an edge was inserted + */ + private void deriveTc(V source, V target, boolean isInsertion) { + + // if (dCount == 1 && isReachable(target, source)) { + // System.out.println("The graph contains cycle with (" + source + ","+ target + ") edge!"); + // } + + CountingTcRelation dtc = new CountingTcRelation(false); + Set tupEnds = null; + + // 1. d(tc(x,y)) :- d(l(x,y)) + if (tc.updateTuple(source, target, isInsertion)) { + dtc.updateTuple(source, target, true /* deltas implicitly have the same sign as isInsertion*/); + notifyTcObservers(source, target, isInsertion); + } + + // 2. d(tc(x,y)) :- d(l(x,z)) & tc(z,y) + tupEnds = tc.getTupleEnds(target); + if (tupEnds != null) { + for (V tupEnd : tupEnds) { + if (!tupEnd.equals(source)) { + if (tc.updateTuple(source, tupEnd, isInsertion)) { + dtc.updateTuple(source, tupEnd, true /* deltas implicitly have the same sign as isInsertion*/); + notifyTcObservers(source, tupEnd, isInsertion); + } + } + } + } + + // 3. d(tc(x,y)) :- lv(x,z) & d(tc(z,y)) + CountingTcRelation newTuples = dtc; + CountingTcRelation tmp = null; + dtc = new CountingTcRelation(false); + + IMemoryView nodes = null; + + while (!newTuples.isEmpty()) { + + tmp = dtc; + dtc = newTuples; + newTuples = tmp; + newTuples.clear(); + + for (V tS : dtc.getTupleStarts()) { + nodes = gds.getSourceNodes(tS); + for (V nS : nodes.distinctValues()) { + int count = nodes.getCount(nS); + for (int i = 0; i < count; i++) { + tupEnds = dtc.getTupleEnds(tS); + if (tupEnds != null) { + for (V tT : tupEnds) { + if (!nS.equals(tT)) { + if (tc.updateTuple(nS, tT, isInsertion)) { + newTuples.updateTuple(nS, tT, true /* deltas implicitly have the same sign as isInsertion*/); + notifyTcObservers(nS, tT, isInsertion); + } + } + } + } + } + } + } + } + + // System.out.println(tc); + } + + public ITcRelation getTcRelation() { + return this.tc; + } + + public void setTcRelation(CountingTcRelation tc) { + this.tc = tc; + } + + @Override + public boolean isReachable(V source, V target) { + return tc.containsTuple(source, target); + } + + @Override + public void attachObserver(ITcObserver to) { + this.observers.add(to); + + } + + @Override + public void detachObserver(ITcObserver to) { + this.observers.remove(to); + } + + @Override + public Set getAllReachableTargets(V source) { + return tc.getTupleEnds(source); + } + + @Override + public Set getAllReachableSources(V target) { + return tc.getTupleStarts(target); + } + + private void notifyTcObservers(V source, V target, boolean isInsertion) { + if (isInsertion) { + for (ITcObserver o : observers) { + o.tupleInserted(source, target); + } + } else { + for (ITcObserver o : observers) { + o.tupleDeleted(source, target); + } + } + } + + @Override + public void dispose() { + tc.clear(); + this.gds.detachObserver(this); + } + + @Override + public IGraphPathFinder getPathFinder() { + return new DFSPathFinder(gds, this); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingTcRelation.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingTcRelation.java new file mode 100644 index 00000000..474c7461 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/counting/CountingTcRelation.java @@ -0,0 +1,259 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.counting; + +import java.util.Collections; +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.rete.itc.alg.misc.topsort.TopologicalSorting; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.ITcRelation; +import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; + +/** + * Transitive closure relation implementation for the Counting algorithm. + * + * @author Tamas Szabo + * + * @param + */ +public class CountingTcRelation implements ITcRelation { + + private IMultiLookup tuplesForward = null; + private IMultiLookup tuplesBackward = null; + + protected CountingTcRelation(boolean backwardIndexing) { + tuplesForward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); + if (backwardIndexing) + tuplesBackward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); + } + + protected boolean isEmpty() { + return 0 == this.tuplesForward.countKeys(); + } + + protected void clear() { + this.tuplesForward.clear(); + + if (tuplesBackward != null) { + this.tuplesBackward.clear(); + } + } + + protected void union(CountingTcRelation rA) { + IMultiLookup rForward = rA.tuplesForward; + for (V source : rForward.distinctKeys()) { + IMemoryView targetBag = rForward.lookup(source); + for (V target : targetBag.distinctValues()) { + this.addTuple(source, target, targetBag.getCount(target)); + } + } + } + + public int getCount(V source, V target) { + IMemoryView bucket = tuplesForward.lookup(source); + return bucket == null ? 0 : bucket.getCount(target); + } + + /** + * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false + * otherwise (in this case count is incremented with the given count parameter). + * + * @param source + * the source of the tuple + * @param target + * the target of the tuple + * @param count + * the count of the tuple, must be positive + * @return true if the relation did not contain previously the tuple + */ + public boolean addTuple(V source, V target, int count) { + if (tuplesBackward != null) { + tuplesBackward.addPairPositiveMultiplicity(target, source, count); + } + + ChangeGranularity change = + tuplesForward.addPairPositiveMultiplicity(source, target, count); + + return change != ChangeGranularity.DUPLICATE; + } + + /** + * Derivation count of the tuple (source,target) is incremented or decremented. + * Returns true iff updated to / from zero derivation count. + * @since 1.7 + */ + public boolean updateTuple(V source, V target, boolean isInsertion) { + if (isInsertion) { + if (tuplesBackward != null) { + tuplesBackward.addPair(target, source); + } + ChangeGranularity change = + tuplesForward.addPair(source, target); + return change != ChangeGranularity.DUPLICATE; + } else { + if (tuplesBackward != null) { + tuplesBackward.removePair(target, source); + } + ChangeGranularity change = + tuplesForward.removePair(source, target); + return change != ChangeGranularity.DUPLICATE; + } + } + + public void deleteTupleEnd(V deleted) { + Set sourcesToDelete = CollectionsFactory.createSet(); + Set targetsToDelete = CollectionsFactory.createSet(); + + for (V target : tuplesForward.lookupOrEmpty(deleted).distinctValues()) { + targetsToDelete.add(target); + } + if (tuplesBackward != null) { + for (V source : tuplesBackward.lookupOrEmpty(deleted).distinctValues()) { + sourcesToDelete.add(source); + } + } else { + for (V sourceCandidate : tuplesForward.distinctKeys()) { + if (tuplesForward.lookupOrEmpty(sourceCandidate).containsNonZero(deleted)) + sourcesToDelete.add(sourceCandidate); + } + } + + for (V source : sourcesToDelete) { + int count = tuplesForward.lookupOrEmpty(source).getCount(deleted); + for (int i=0; i< count; ++i) tuplesForward.removePair(source, deleted); + } + for (V target : targetsToDelete) { + int count = tuplesForward.lookupOrEmpty(deleted).getCount(target); + for (int i=0; i< count; ++i) tuplesForward.removePair(deleted, target); + } + + if (tuplesBackward != null) { + for (V source : sourcesToDelete) { + int count = tuplesBackward.lookupOrEmpty(deleted).getCount(source); + for (int i=0; i< count; ++i) tuplesBackward.removePair(deleted, source); + } + for (V target : targetsToDelete) { + int count = tuplesBackward.lookupOrEmpty(target).getCount(deleted); + for (int i=0; i< count; ++i) tuplesBackward.removePair(target, deleted); + } + } + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("TcRelation = "); + + for (V source : tuplesForward.distinctKeys()) { + IMemoryView targets = tuplesForward.lookup(source); + for (V target : targets.distinctValues()) { + sb.append("{(" + source + "," + target + ")," + targets.getCount(target) + "} "); + } + } + + return sb.toString(); + } + + @Override + public Set getTupleEnds(V source) { + IMemoryView tupEnds = tuplesForward.lookup(source); + if (tupEnds == null) + return null; + return tupEnds.distinctValues(); + } + + /** + * Returns the set of nodes from which the target node is reachable, if already computed. + * + * @param target + * the target node + * @return the set of source nodes + * @throws UnsupportedOperationException if backwards index not computed + */ + public Set getTupleStarts(V target) { + if (tuplesBackward != null) { + IMemoryView tupStarts = tuplesBackward.lookup(target); + if (tupStarts == null) + return null; + return tupStarts.distinctValues(); + } else { + throw new UnsupportedOperationException("built without backward indexing"); + } + } + + @Override + public Set getTupleStarts() { + Set nodes = CollectionsFactory.createSet(); + for (V s : tuplesForward.distinctKeys()) { + nodes.add(s); + } + return nodes; + } + + /** + * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise. + * + * @param source + * the source node + * @param target + * the target node + * @return true if tuple is present, false otherwise + */ + public boolean containsTuple(V source, V target) { + return tuplesForward.lookupOrEmpty(source).containsNonZero(target); + } + + @SuppressWarnings("unchecked") + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null || this.getClass() != obj.getClass()) { + return false; + } else { + CountingTcRelation aTR = (CountingTcRelation) obj; + + return tuplesForward.equals(aTR.tuplesForward); + } + } + + @Override + public int hashCode() { + return tuplesForward.hashCode(); + } + + public static CountingTcRelation createFrom(IBiDirectionalGraphDataSource gds) { + List topologicalSorting = TopologicalSorting.compute(gds); + CountingTcRelation tc = new CountingTcRelation(true); + Collections.reverse(topologicalSorting); + for (V n : topologicalSorting) { + IMemoryView sourceNodes = gds.getSourceNodes(n); + Set tupEnds = tc.getTupleEnds(n); + for (V s : sourceNodes.distinctValues()) { + int count = sourceNodes.getCount(s); + for (int i = 0; i < count; i++) { + tc.updateTuple(s, n, true); + if (tupEnds != null) { + for (V t : tupEnds) { + tc.updateTuple(s, t, true); + } + } + } + } + } + + return tc; + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/CountingListener.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/CountingListener.java new file mode 100644 index 00000000..7d507d82 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/CountingListener.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.itc.alg.incscc; + +import tools.refinery.viatra.runtime.rete.itc.igraph.ITcObserver; +import tools.refinery.viatra.runtime.matchers.util.Direction; + +/** + * @author Tamas Szabo + * + */ +public class CountingListener implements ITcObserver { + + private IncSCCAlg alg; + + public CountingListener(IncSCCAlg alg) { + this.alg = alg; + } + + @Override + public void tupleInserted(V source, V target) { + alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.INSERT); + } + + @Override + public void tupleDeleted(V source, V target) { + alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.DELETE); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/IncSCCAlg.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/IncSCCAlg.java new file mode 100644 index 00000000..774e55eb --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/incscc/IncSCCAlg.java @@ -0,0 +1,609 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.incscc; + +import tools.refinery.viatra.runtime.matchers.algorithms.UnionFind; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.Direction; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.rete.itc.alg.counting.CountingAlg; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.DFSPathFinder; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.GraphHelper; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.IGraphPathFinder; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.bfs.BFS; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCC; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCCResult; +import tools.refinery.viatra.runtime.rete.itc.alg.util.CollectionHelper; +import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.rete.itc.igraph.*; + +import java.util.*; +import java.util.Map.Entry; + +/** + * Incremental SCC maintenance + counting algorithm. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the nodes in the graph data source + */ +public class IncSCCAlg implements IGraphObserver, ITcDataSource { + + public UnionFind sccs; + public IBiDirectionalGraphDataSource gds; + private CountingAlg counting; + private Graph reducedGraph; + private IBiDirectionalGraphDataSource reducedGraphIndexer; + private List> observers; + private CountingListener countingListener; + + public IncSCCAlg(IGraphDataSource graphDataSource) { + + if (graphDataSource instanceof IBiDirectionalGraphDataSource) { + gds = (IBiDirectionalGraphDataSource) graphDataSource; + } else { + gds = new IBiDirectionalWrapper(graphDataSource); + } + observers = CollectionsFactory.createObserverList(); + sccs = new UnionFind(); + reducedGraph = new Graph(); + reducedGraphIndexer = new IBiDirectionalWrapper(reducedGraph); + countingListener = new CountingListener(this); + initalizeInternalDataStructures(); + gds.attachObserver(this); + } + + private void initalizeInternalDataStructures() { + SCCResult _sccres = SCC.computeSCC(gds); + Set> _sccs = _sccres.getSccs(); + + for (Set _set : _sccs) { + sccs.makeSet(_set); + } + + // Initalization of the reduced graph + for (V n : sccs.getPartitionHeads()) { + reducedGraph.insertNode(n); + } + + for (V source : gds.getAllNodes()) { + final IMemoryView targetNodes = gds.getTargetNodes(source); + for (Entry entry : targetNodes.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V target = entry.getKey(); + V sourceRoot = sccs.find(source); + V targetRoot = sccs.find(target); + + if (!sourceRoot.equals(targetRoot)) { + reducedGraph.insertEdge(sourceRoot, targetRoot); + } + } + } + } + + counting = new CountingAlg(reducedGraph); + } + + @Override + public void edgeInserted(V source, V target) { + V sourceRoot = sccs.find(source); + V targetRoot = sccs.find(target); + + // Different SCC + if (!sourceRoot.equals(targetRoot)) { + + // source is reachable from target? + if (counting.isReachable(targetRoot, sourceRoot)) { + + Set predecessorRoots = counting.getAllReachableSources(sourceRoot); + Set successorRoots = counting.getAllReachableTargets(targetRoot); + + // 1. intersection of source and target roots, these will be in the merged SCC + Set isectRoots = CollectionHelper.intersection(predecessorRoots, successorRoots); + isectRoots.add(sourceRoot); + isectRoots.add(targetRoot); + + // notifications must be issued before Union-Find modifications + if (observers.size() > 0) { + Set sourceSCCs = createSetNullTolerant(predecessorRoots); + sourceSCCs.add(sourceRoot); + Set targetSCCs = createSetNullTolerant(successorRoots); + targetSCCs.add(targetRoot); + + // tracing back to actual nodes + for (V sourceSCC : sourceSCCs) { + targetLoop: for (V targetSCC : targetSCCs) { + if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; + + boolean needsNotification = + // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. + // Issue notifications only if there is no self-loop present at the moment + (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper + .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) + || + // Case 2. sourceSCC and targetSCC are different sccs. + (!sourceSCC.equals(targetSCC)); + // if self loop is already present omit the notification + if (needsNotification) { + notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), + Direction.INSERT); + } + } + } + } + + // 2. delete edges, nodes + List sourceSCCs = new ArrayList(); + List targetSCCs = new ArrayList(); + + for (V r : isectRoots) { + List sourceSCCsOfSCC = getSourceSCCsOfSCC(r); + List targetSCCsOfSCC = getTargetSCCsOfSCC(r); + + for (V sourceSCC : sourceSCCsOfSCC) { + if (!sourceSCC.equals(r)) { + reducedGraph.deleteEdgeIfExists(sourceSCC, r); + } + } + + for (V targetSCC : targetSCCsOfSCC) { + if (!isectRoots.contains(targetSCC) && !r.equals(targetSCC)) { + reducedGraph.deleteEdgeIfExists(r, targetSCC); + } + } + + sourceSCCs.addAll(sourceSCCsOfSCC); + targetSCCs.addAll(targetSCCsOfSCC); + } + + for (V r : isectRoots) { + reducedGraph.deleteNode(r); + } + + // 3. union + Iterator iterator = isectRoots.iterator(); + V newRoot = iterator.next(); + while (iterator.hasNext()) { + newRoot = sccs.union(newRoot, iterator.next()); + } + + // 4. add new node + reducedGraph.insertNode(newRoot); + + // 5. add edges + Set containedNodes = sccs.getPartition(newRoot); + + for (V sourceSCC : sourceSCCs) { + if (!containedNodes.contains(sourceSCC) && !sourceSCC.equals(newRoot)) { + reducedGraph.insertEdge(sourceSCC, newRoot); + } + } + for (V targetSCC : targetSCCs) { + if (!containedNodes.contains(targetSCC) && !targetSCC.equals(newRoot)) { + reducedGraph.insertEdge(newRoot, targetSCC); + } + } + } else { + if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 1) { + counting.attachObserver(countingListener); + } + reducedGraph.insertEdge(sourceRoot, targetRoot); + counting.detachObserver(countingListener); + } + } else { + // Notifications about self-loops + if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 + && GraphHelper.getEdgeCount(source, target, gds) == 1) { + notifyTcObservers(source, source, Direction.INSERT); + } + } + } + + @Override + public void edgeDeleted(V source, V target) { + V sourceRoot = sccs.find(source); + V targetRoot = sccs.find(target); + + if (!sourceRoot.equals(targetRoot)) { + if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 0) { + counting.attachObserver(countingListener); + } + reducedGraph.deleteEdgeIfExists(sourceRoot, targetRoot); + counting.detachObserver(countingListener); + } else { + // get the graph for the scc whose root is sourceRoot + Graph g = GraphHelper.getSubGraph(sccs.getPartition(sourceRoot), gds); + + // if source is not reachable from target anymore + if (!BFS.isReachable(source, target, g)) { + // create copies of the current state before destructive manipulation + Map reachableSources = CollectionsFactory.createMap(); + for (Entry entry : reducedGraphIndexer.getSourceNodes(sourceRoot).entriesWithMultiplicities()) { + reachableSources.put(entry.getKey(), entry.getValue()); + } + Map reachableTargets = CollectionsFactory.createMap(); + for (Entry entry : reducedGraphIndexer.getTargetNodes(sourceRoot).entriesWithMultiplicities()) { + reachableTargets.put(entry.getKey(), entry.getValue()); + } + + SCCResult _newSccs = SCC.computeSCC(g); + + // delete scc node (and with its edges too) + for (Entry entry : reachableSources.entrySet()) { + V s = entry.getKey(); + for (int i = 0; i < entry.getValue(); i++) { + reducedGraph.deleteEdgeIfExists(s, sourceRoot); + } + } + + for (Entry entry : reachableTargets.entrySet()) { + V t = entry.getKey(); + for (int i = 0; i < entry.getValue(); i++) { + reducedGraph.deleteEdgeIfExists(sourceRoot, t); + } + } + + sccs.deleteSet(sourceRoot); + reducedGraph.deleteNode(sourceRoot); + + Set> newSCCs = _newSccs.getSccs(); + Set newSCCRoots = CollectionsFactory.createSet(); + + // add new nodes and edges to the reduced graph + for (Set newSCC : newSCCs) { + V newRoot = sccs.makeSet(newSCC); + reducedGraph.insertNode(newRoot); + newSCCRoots.add(newRoot); + } + for (V newSCCRoot : newSCCRoots) { + List sourceSCCsOfSCC = getSourceSCCsOfSCC(newSCCRoot); + List targetSCCsOfSCC = getTargetSCCsOfSCC(newSCCRoot); + + for (V sourceSCC : sourceSCCsOfSCC) { + if (!sourceSCC.equals(newSCCRoot)) { + reducedGraph.insertEdge(sccs.find(sourceSCC), newSCCRoot); + } + } + for (V targetSCC : targetSCCsOfSCC) { + if (!newSCCRoots.contains(targetSCC) && !targetSCC.equals(newSCCRoot)) + reducedGraph.insertEdge(newSCCRoot, targetSCC); + } + } + + // Must be after the union-find modifications + if (observers.size() > 0) { + V newSourceRoot = sccs.find(source); + V newTargetRoot = sccs.find(target); + + Set sourceSCCs = createSetNullTolerant(counting.getAllReachableSources(newSourceRoot)); + sourceSCCs.add(newSourceRoot); + + Set targetSCCs = createSetNullTolerant(counting.getAllReachableTargets(newTargetRoot)); + targetSCCs.add(newTargetRoot); + + for (V sourceSCC : sourceSCCs) { + targetLoop: for (V targetSCC : targetSCCs) { + if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; + + boolean needsNotification = + // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. + // Issue notifications only if there is no self-loop present at the moment + (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper + .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) + || + // Case 2. sourceSCC and targetSCC are different sccs. + (!sourceSCC.equals(targetSCC)); + // if self loop is already present omit the notification + if (needsNotification) { + notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), + Direction.DELETE); + } + } + } + } + } else { + // only handle self-loop notifications - sourceRoot equals to targetRoot + if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 + && GraphHelper.getEdgeCount(source, target, gds) == 0) { + notifyTcObservers(source, source, Direction.DELETE); + } + } + } + } + + @Override + public void nodeInserted(V n) { + sccs.makeSet(n); + reducedGraph.insertNode(n); + } + + @Override + public void nodeDeleted(V n) { + IMemoryView sources = gds.getSourceNodes(n); + IMemoryView targets = gds.getTargetNodes(n); + + for (Entry entry : sources.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V source = entry.getKey(); + edgeDeleted(source, n); + } + } + + for (Entry entry : targets.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V target = entry.getKey(); + edgeDeleted(n, target); + } + } + + sccs.deleteSet(n); + } + + @Override + public void attachObserver(ITcObserver to) { + observers.add(to); + } + + @Override + public void detachObserver(ITcObserver to) { + observers.remove(to); + } + + @Override + public Set getAllReachableTargets(V source) { + V sourceRoot = sccs.find(source); + Set containedNodes = sccs.getPartition(sourceRoot); + Set targets = CollectionsFactory.createSet(); + + if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(source, gds) == 1) { + targets.addAll(containedNodes); + } + + Set rootSet = counting.getAllReachableTargets(sourceRoot); + if (rootSet != null) { + for (V _root : rootSet) { + targets.addAll(sccs.getPartition(_root)); + } + } + + return targets; + } + + @Override + public Set getAllReachableSources(V target) { + V targetRoot = sccs.find(target); + Set containedNodes = sccs.getPartition(targetRoot); + Set sources = CollectionsFactory.createSet(); + + if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(target, gds) == 1) { + sources.addAll(containedNodes); + } + + Set rootSet = counting.getAllReachableSources(targetRoot); + if (rootSet != null) { + for (V _root : rootSet) { + sources.addAll(sccs.getPartition(_root)); + } + } + return sources; + } + + @Override + public boolean isReachable(V source, V target) { + V sourceRoot = sccs.find(source); + V targetRoot = sccs.find(target); + + if (sourceRoot.equals(targetRoot)) + return true; + else + return counting.isReachable(sourceRoot, targetRoot); + } + + public List getReachabilityPath(V source, V target) { + if (!isReachable(source, target)) { + return null; + } else { + Set sccsInSubGraph = CollectionHelper.intersection(counting.getAllReachableTargets(source), + counting.getAllReachableSources(target)); + sccsInSubGraph.add(sccs.find(source)); + sccsInSubGraph.add(sccs.find(target)); + Set nodesInSubGraph = CollectionsFactory.createSet(); + + for (V sccRoot : sccsInSubGraph) { + nodesInSubGraph.addAll(sccs.getPartition(sccRoot)); + } + + return GraphHelper.constructPath(source, target, nodesInSubGraph, gds); + } + } + + /** + * Return the SCCs from which the SCC represented by the root node is reachable. Note that an SCC can be present + * multiple times in the returned list (multiple edges between the two SCCs). + * + * @param root + * @return the list of reachable target SCCs + */ + private List getSourceSCCsOfSCC(V root) { + List sourceSCCs = new ArrayList(); + + for (V containedNode : this.sccs.getPartition(root)) { + IMemoryView sourceNodes = this.gds.getSourceNodes(containedNode); + for (V source : sourceNodes.distinctValues()) { + sourceSCCs.add(this.sccs.find(source)); + } + } + + return sourceSCCs; + } + + /** + * Returns true if the SCC represented by the given root node has incoming edges in the reduced graph, + * false otherwise (if this SCC is a source in the reduced graph). + * + * @param root the root node of an SCC + * @return true if it has incoming edges, false otherwise + * @since 1.6 + */ + public boolean hasIncomingEdges(final V root) { + for (final V containedNode : this.sccs.getPartition(root)) { + final IMemoryView sourceNodes = this.gds.getSourceNodes(containedNode); + for (final V source : sourceNodes.distinctValues()) { + final V otherRoot = this.sccs.find(source); + if (!Objects.equals(root, otherRoot)) { + return true; + } + } + } + return false; + } + + /** + * Return the SCCs which are reachable from the SCC represented by the root node. Note that an SCC can be present + * multiple times in the returned list (multiple edges between the two SCCs). + * + * @param root + * @return the list of reachable target SCCs + */ + private List getTargetSCCsOfSCC(V root) { + List targetSCCs = new ArrayList(); + + for (V containedNode : this.sccs.getPartition(root)) { + IMemoryView targetNodes = this.gds.getTargetNodes(containedNode); + for (V target : targetNodes.distinctValues()) { + targetSCCs.add(this.sccs.find(target)); + } + } + + return targetSCCs; + } + + /** + * Returns true if the SCC represented by the given root node has outgoing edges in the reduced graph, + * false otherwise (if this SCC is a sink in the reduced graph). + * + * @param root the root node of an SCC + * @return true if it has outgoing edges, false otherwise + * @since 1.6 + */ + public boolean hasOutgoingEdges(V root) { + for (final V containedNode : this.sccs.getPartition(root)) { + final IMemoryView targetNodes = this.gds.getTargetNodes(containedNode); + for (final V target : targetNodes.distinctValues()) { + final V otherRoot = this.sccs.find(target); + if (!Objects.equals(root, otherRoot)) { + return true; + } + } + } + return false; + } + + @Override + public void dispose() { + gds.detachObserver(this); + counting.dispose(); + } + + /** + * Call this method to notify the observers of the transitive closure relation. The tuples used in the notification + * will be the Descartes product of the two sets given. + * + * @param sources + * the source nodes + * @param targets + * the target nodes + * @param direction + */ + protected void notifyTcObservers(Set sources, Set targets, Direction direction) { + for (V s : sources) { + for (V t : targets) { + notifyTcObservers(s, t, direction); + } + } + } + + private void notifyTcObservers(V source, V target, Direction direction) { + for (ITcObserver observer : observers) { + if (direction == Direction.INSERT) { + observer.tupleInserted(source, target); + } + if (direction == Direction.DELETE) { + observer.tupleDeleted(source, target); + } + } + } + + /** + * Returns the node that is selected as the representative of the SCC containing the argument. + * @since 1.6 + */ + public V getRepresentative(V node) { + return sccs.find(node); + } + + public Set> getTcRelation() { + Set> resultSet = new HashSet>(); + + for (V sourceRoot : sccs.getPartitionHeads()) { + Set sources = sccs.getPartition(sourceRoot); + if (sources.size() > 1 || GraphHelper.getEdgeCount(sources.iterator().next(), gds) == 1) { + for (V source : sources) { + for (V target : sources) { + resultSet.add(new Tuple(source, target)); + } + } + } + + Set reachableTargets = counting.getAllReachableTargets(sourceRoot); + if (reachableTargets != null) { + for (V targetRoot : reachableTargets) { + for (V source : sources) { + for (V target : sccs.getPartition(targetRoot)) { + resultSet.add(new Tuple(source, target)); + } + } + } + } + } + + return resultSet; + } + + public boolean isIsolated(V node) { + IMemoryView targets = gds.getTargetNodes(node); + IMemoryView sources = gds.getSourceNodes(node); + return targets.isEmpty() && sources.isEmpty(); + } + + @Override + public IGraphPathFinder getPathFinder() { + return new DFSPathFinder(gds, this); + } + + /** + * The graph of SCCs; each SCC is represented by its representative node (see {@link #getRepresentative(Object)}) + * @since 1.6 + */ + public Graph getReducedGraph() { + return reducedGraph; + } + + private static Set createSetNullTolerant(Set initial) { + if (initial != null) + return CollectionsFactory.createSet(initial); + else + return CollectionsFactory.createSet(); + } + + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/DFSPathFinder.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/DFSPathFinder.java new file mode 100644 index 00000000..2cec33a2 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/DFSPathFinder.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2010-2016, Abel Hegedus and IncQueryLabs Ltd. + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.itc.alg.misc; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.rete.itc.igraph.ITcDataSource; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; + +/** + * A depth-first search implementation of the {@link IGraphPathFinder}. + * + * TODO use ITC to filter nodes that must be traversed, instead of checks + * + * @author Abel Hegedus + * + * @param + * the node type of the graph + */ +public class DFSPathFinder implements IGraphPathFinder { + + private IGraphDataSource graph; + private ITcDataSource itc; + + public DFSPathFinder(IGraphDataSource graph, ITcDataSource itc) { + this.graph = graph; + this.itc = itc; + } + + @Override + public Iterable> getAllPaths(V sourceNode, V targetNode) { + Set endNodes = new HashSet(); + endNodes.add(targetNode); + return getAllPathsToTargets(sourceNode, endNodes); + } + + @Override + public Iterable> getAllPathsToTargets(V sourceNode, Set targetNodes) { + List> paths = new ArrayList>(); + Deque visited = new LinkedList(); + Set reachableTargets = new HashSet(); + for (V targetNode : targetNodes) { + if (itc.isReachable(sourceNode, targetNode)) { + reachableTargets.add(targetNode); + } + } + if (!reachableTargets.isEmpty()) { + return paths; + } + visited.add(sourceNode); + return getPaths(paths, visited, reachableTargets); + } + + protected Iterable> getPaths(List> paths, Deque visited, Set targetNodes) { + IMemoryView nodes = graph.getTargetNodes(visited.getLast()); + // examine adjacent nodes + for (V node : nodes.distinctValues()) { + if (visited.contains(node)) { + continue; + } + if (targetNodes.contains(node)) { + visited.add(node); + // clone visited LinkedList + Deque visitedClone = new LinkedList(visited); + paths.add(visitedClone); + visited.removeLast(); + break; + } + } + + // in breadth-first, recursion needs to come after visiting connected nodes + for (V node : nodes.distinctValues()) { + if (visited.contains(node) || targetNodes.contains(node)) { + continue; + } + boolean canReachTarget = false; + for (V target : targetNodes) { + if (itc.isReachable(node, target)) { + canReachTarget = true; + break; + } + } + if (canReachTarget) { + visited.addLast(node); + getPaths(paths, visited, targetNodes); + visited.removeLast(); + } + } + + return paths; + } + + public String printPaths(List> paths) { + StringBuilder sb = new StringBuilder(); + for (Deque visited : paths) { + sb.append("Path: "); + for (V node : visited) { + sb.append(node); + sb.append(" --> "); + } + sb.append("\n"); + } + return sb.toString(); + } + + @Override + public Deque getPath(V sourceNode, V targetNode) { + // TODO optimize + Iterable> allPaths = getAllPaths(sourceNode, targetNode); + Iterator> pathIterator = allPaths.iterator(); + return pathIterator.hasNext() ? pathIterator.next() : new LinkedList(); + } + + @Override + public Iterable> getShortestPaths(V sourceNode, V targetNode) { + // TODO optimize + Iterable> allPaths = getAllPaths(sourceNode, targetNode); + List> shortestPaths = new ArrayList>(); + int shortestPathLength = -1; + for (Deque path : allPaths) { + int pathLength = path.size(); + if (shortestPathLength == -1 || pathLength < shortestPathLength) { + shortestPaths.clear(); + shortestPathLength = pathLength; + } + if (pathLength == shortestPathLength) { + shortestPaths.add(path); + } + } + return shortestPaths; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Edge.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Edge.java new file mode 100644 index 00000000..862c99b3 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Edge.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.misc; + +public class Edge { + private V source; + private V target; + + public Edge(V source, V target) { + super(); + this.source = source; + this.target = target; + } + + public V getSource() { + return source; + } + + public void setSource(V source) { + this.source = source; + } + + public V getTarget() { + return target; + } + + public void setTarget(V target) { + this.target = target; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/GraphHelper.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/GraphHelper.java new file mode 100644 index 00000000..b79e4d45 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/GraphHelper.java @@ -0,0 +1,169 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.itc.alg.misc; + +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; + +import java.util.*; +import java.util.Map.Entry; + +/** + * Utility class for graph related operations. + * + * @author Tamas Szabo + */ +public class GraphHelper { + + private GraphHelper() {/*Utility class constructor*/} + + /** + * Returns the subgraph from the given {@link IBiDirectionalGraphDataSource} which contains the given set of nodes. + * + * @param nodesInSubGraph + * the nodes that are present in the subgraph + * @param graphDataSource + * the graph data source for the original graph + * @return the subgraph associated to the given nodes + */ + public static Graph getSubGraph(Collection nodesInSubGraph, + IBiDirectionalGraphDataSource graphDataSource) { + Graph g = new Graph(); + if (nodesInSubGraph != null) { + for (V node : nodesInSubGraph) { + g.insertNode(node); + } + + for (V node : nodesInSubGraph) { + IMemoryView sources = graphDataSource.getSourceNodes(node); + for (Entry entry : sources.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V s = entry.getKey(); + if (nodesInSubGraph.contains(s)) { + g.insertEdge(s, node); + } + } + } + } + } + + return g; + } + + /** + * Constructs a path between source and target in the given graph. Both the {@link IGraphDataSource} and the set of + * nodes are used, this way it is possible to construct a path in a given subgraph. + * + * The returned {@link List} contains the nodes along the path (this means that there is an edge in the graph + * between two consecutive nodes). A self loop (one edge) is indicated with the source node being present two times + * in the returned {@link List}. + * + * @param source + * the source node + * @param target + * the target node + * @param nodesInGraph + * the nodes that are present in the subgraph + * @param graphDataSource + * the graph data source + * @return the path between the two nodes + */ + public static List constructPath(V source, V target, Set nodesInGraph, + IGraphDataSource graphDataSource) { + Set visitedNodes = new HashSet(); + List path = new ArrayList(); + + visitedNodes.add(source); + path.add(source); + V act = source; + + // if source and target are the same node + if (source.equals(target) && graphDataSource.getTargetNodes(source).containsNonZero(target)) { + // the node will be present in the path two times + path.add(source); + return path; + } else { + while (act != null) { + V nextNode = getNextNodeToVisit(act, graphDataSource, nodesInGraph, visitedNodes); + if (nextNode == null && path.size() > 1) { + // needs to backtrack along path + // remove the last element in the path because we can't go + // anywhere from there + path.remove(path.size() - 1); + while (nextNode == null && path.size() > 0) { + V lastPathElement = path.get(path.size() - 1); + nextNode = getNextNodeToVisit(lastPathElement, graphDataSource, nodesInGraph, visitedNodes); + if (nextNode == null) { + path.remove(path.size() - 1); + } + } + } + + if (nextNode != null) { + visitedNodes.add(nextNode); + path.add(nextNode); + if (nextNode.equals(target)) { + return path; + } + } + act = nextNode; + } + return null; + } + } + + private static V getNextNodeToVisit(V act, IGraphDataSource graphDataSource, Set nodesInSubGraph, + Set visitedNodes) { + IMemoryView targetNodes = graphDataSource.getTargetNodes(act); + for (Entry entry : targetNodes.entriesWithMultiplicities()) { + for (int i = 0; i < entry.getValue(); i++) { + V node = entry.getKey(); + if (nodesInSubGraph.contains(node) && !visitedNodes.contains(node)) { + return node; + } + } + } + return null; + } + + /** + * Returns the number of self-loop edges for the given node. + * + * @param node + * the node + * @param graphDataSource + * the graph data source + * @return the number of self-loop edges + */ + public static int getEdgeCount(V node, IGraphDataSource graphDataSource) { + return getEdgeCount(node, node, graphDataSource); + } + + /** + * Returns the number of edges between the given source and target nodes. + * + * @param source + * the source node + * @param target + * the target node + * @param graphDataSource + * the graph data source + * @return the number of parallel edges between the two nodes + */ + public static int getEdgeCount(V source, V target, IGraphDataSource graphDataSource) { + Integer count = graphDataSource.getTargetNodes(source).getCount(target); + if (count == null) { + return 0; + } else { + return count; + } + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/IGraphPathFinder.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/IGraphPathFinder.java new file mode 100644 index 00000000..624f9f7d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/IGraphPathFinder.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.itc.alg.misc; + +import java.util.Deque; +import java.util.Set; + +import tools.refinery.viatra.runtime.rete.itc.igraph.ITcDataSource; + +/** + * The path finder provides methods for retrieving paths in a graph between a source node and one or more target nodes. + * Use {@link ITcDataSource#getPathFinder()} for instantiating. + * + * @author Abel Hegedus + * + * @param the node type of the graph + */ +public interface IGraphPathFinder { + + /** + * Returns an arbitrary path from the source node to the target node (if such exists). If there is no path + * between them, an empty collection is returned. + * + * @param sourceNode the source node of the path + * @param targetNode the target node of the path + * @return the path from the source to the target, or empty collection if target is not reachable from source. + */ + Deque getPath(V sourceNode, V targetNode); + + /** + * Returns the collection of shortest paths from the source node to the target node (if such exists). If there is no path + * between them, an empty collection is returned. + * + * @param sourceNode the source node of the path + * @param targetNode the target node of the path + * @return the collection of shortest paths from the source to the target, or empty collection if target is not reachable from source. + */ + Iterable> getShortestPaths(V sourceNode, V targetNode); + + /** + * Returns the collection of paths from the source node to the target node (if such exists). If there is no path + * between them, an empty collection is returned. + * + * @param sourceNode the source node of the path + * @param targetNode the target node of the path + * @return the collection of paths from the source to the target, or empty collection if target is not reachable from source. + */ + Iterable> getAllPaths(V sourceNode, V targetNode); + + /** + * Returns the collection of paths from the source node to any of the target nodes (if such exists). If there is no path + * between them, an empty collection is returned. + * + * @param sourceNode the source node of the path + * @param targetNodes the set of target nodes of the paths + * @return the collection of paths from the source to any of the targets, or empty collection if neither target is reachable from source. + */ + Iterable> getAllPathsToTargets(V sourceNode, Set targetNodes); + + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/ITcRelation.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/ITcRelation.java new file mode 100644 index 00000000..9fd85ae1 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/ITcRelation.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.misc; + +import java.util.Set; + +public interface ITcRelation { + + /** + * Returns the starting nodes from a transitive closure relation. + * + * @return the set of starting nodes + */ + public Set getTupleStarts(); + + /** + * Returns the set of nodes that are reachable from the given node. + * + * @param start + * the starting node + * @return the set of reachable nodes + */ + public Set getTupleEnds(V start); +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Tuple.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Tuple.java new file mode 100644 index 00000000..84c79dcf --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/Tuple.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.misc; + +public class Tuple { + + private V source; + private V target; + + public Tuple(V source, V target) { + super(); + this.source = source; + this.target = target; + } + + public V getSource() { + return source; + } + + public void setSource(V source) { + this.source = source; + } + + public V getTarget() { + return target; + } + + public void setTarget(V target) { + this.target = target; + } + + @Override + public String toString() { + return "(" + source.toString() + "," + target.toString() + ")"; + } + + @Override + public boolean equals(Object o) { + if (o instanceof Tuple) { + Tuple t = (Tuple) o; + + if (t.getSource().equals(this.source) && t.getTarget().equals(this.target)) { + return true; + } + } + return false; + } + + @Override + public int hashCode() { + return source.hashCode() + target.hashCode(); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java new file mode 100644 index 00000000..00b0a96d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.misc.bfs; + +import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class BFS { + + private BFS() {/*Utility class constructor*/} + + /** + * Performs a breadth first search on the given graph to determine whether source is reachable from target. + * + * @param + * the type parameter of the nodes in the graph + * @param source + * the source node + * @param target + * the target node + * @param graph + * the graph data source + * @return true if source is reachable from target, false otherwise + */ + public static boolean isReachable(V source, V target, IGraphDataSource graph) { + List nodeQueue = new ArrayList(); + Set visited = new HashSet(); + + nodeQueue.add(source); + visited.add(source); + + boolean ret = _isReachable(target, graph, nodeQueue, visited); + return ret; + } + + private static boolean _isReachable(V target, IGraphDataSource graph, List nodeQueue, Set visited) { + + while (!nodeQueue.isEmpty()) { + V node = nodeQueue.remove(0); + for (V t : graph.getTargetNodes(node).distinctValues()){ + if (t.equals(target)) { + return true; + } + if (!visited.contains(t)) { + visited.add(t); + nodeQueue.add(t); + } + } + } + return false; + } + + public static Set reachableSources(IBiDirectionalGraphDataSource graph, V target) { + Set retSet = new HashSet(); + retSet.add(target); + List nodeQueue = new ArrayList(); + nodeQueue.add(target); + + _reachableSources(graph, nodeQueue, retSet); + + return retSet; + } + + private static void _reachableSources(IBiDirectionalGraphDataSource graph, List nodeQueue, + Set retSet) { + while (!nodeQueue.isEmpty()) { + V node = nodeQueue.remove(0); + for (V _node : graph.getSourceNodes(node).distinctValues()) { + if (!retSet.contains(_node)) { + retSet.add(_node); + nodeQueue.add(_node); + } + } + } + } + + public static Set reachableTargets(IGraphDataSource graph, V source) { + Set retSet = new HashSet(); + retSet.add(source); + List nodeQueue = new ArrayList(); + nodeQueue.add(source); + + _reachableTargets(graph, nodeQueue, retSet); + + return retSet; + } + + private static void _reachableTargets(IGraphDataSource graph, List nodeQueue, Set retSet) { + while (!nodeQueue.isEmpty()) { + V node = nodeQueue.remove(0); + + for (V _node : graph.getTargetNodes(node).distinctValues()) { + + if (!retSet.contains(_node)) { + retSet.add(_node); + nodeQueue.add(_node); + } + } + } + } + + /** + * Performs a breadth first search on the given graph and collects all the nodes along the path from source to + * target if such path exists. + * + * @param + * the type parameter of the nodes in the graph + * @param source + * the source node + * @param target + * the target node + * @param graph + * the graph data source + * @return the set of nodes along the path + */ + public static Set collectNodesAlongPath(V source, V target, IGraphDataSource graph) { + Set path = new HashSet(); + _collectNodesAlongPath(source, target, graph, path); + return path; + } + + private static boolean _collectNodesAlongPath(V node, V target, IGraphDataSource graph, Set path) { + + boolean res = false; + + // end recursion + if (node.equals(target)) { + path.add(node); + return true; + } else { + for (V _nodeT : graph.getTargetNodes(node).distinctValues()) { + res = (_collectNodesAlongPath(_nodeT, target, graph, path)) || res; + } + if (res) + path.add(node); + return res; + } + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/PKAlg.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/PKAlg.java new file mode 100644 index 00000000..892d048e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/PKAlg.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.misc.scc; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalWrapper; +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphObserver; + +public class PKAlg implements IGraphObserver { + + /** + * Maps the nodes to their indicies. + */ + private Map node2index; + private Map index2node; + private Map node2mark; + + /** + * Maps the index of a node to the index in the topsort. + */ + private Map index2topsort; + private Map topsort2index; + + /** + * Index associated to the inserted nodes (incrementing with every insertion). + */ + private int index; + + /** + * Index within the topsort for the target node when edge insertion occurs. + */ + private int lower_bound; + + /** + * Index within the topsort for the source node when edge insertion occurs. + */ + private int upper_bound; + + private List RF; + private List RB; + private IBiDirectionalGraphDataSource gds; + + public PKAlg(IGraphDataSource gds) { + if (gds instanceof IBiDirectionalGraphDataSource) { + this.gds = (IBiDirectionalGraphDataSource) gds; + } else { + this.gds = new IBiDirectionalWrapper(gds); + } + + node2mark = new HashMap(); + node2index = new HashMap(); + index2node = new HashMap(); + index2topsort = new HashMap(); + topsort2index = new HashMap(); + index = 0; + + gds.attachObserver(this); + } + + @Override + public void edgeInserted(V source, V target) { + + RF = new ArrayList(); + RB = new ArrayList(); + + lower_bound = index2topsort.get(node2index.get(target)); + upper_bound = index2topsort.get(node2index.get(source)); + + if (lower_bound < upper_bound) { + dfsForward(target); + dfsBackward(source); + reorder(); + } + } + + private List getIndicies(List list) { + List indicies = new ArrayList(); + + for (V n : list) + indicies.add(index2topsort.get(node2index.get(n))); + + return indicies; + } + + private void reorder() { + + Collections.reverse(RB); + + // azon csomopontok indexei amelyek sorrendje nem jo + List L = getIndicies(RF); + L.addAll(getIndicies(RB)); + Collections.sort(L); + + for (int i = 0; i < RB.size(); i++) { + index2topsort.put(node2index.get(RB.get(i)), L.get(i)); + topsort2index.put(L.get(i), node2index.get(RB.get(i))); + } + + for (int i = 0; i < RF.size(); i++) { + index2topsort.put(node2index.get(RF.get(i)), L.get(i + RB.size())); + topsort2index.put(L.get(i + RB.size()), node2index.get(RF.get(i))); + } + } + + @SuppressWarnings("unused") + private List getTopSort() { + List topsort = new ArrayList(); + + for (int i : topsort2index.values()) { + topsort.add(index2node.get(i)); + } + + return topsort; + } + + private void dfsBackward(V node) { + node2mark.put(node, true); + RB.add(node); + + for (V sn : gds.getSourceNodes(node).distinctValues()) { + int top_id = index2topsort.get(node2index.get(sn)); + + if (!node2mark.get(sn) && lower_bound < top_id) + dfsBackward(sn); + } + } + + private void dfsForward(V node) { + node2mark.put(node, true); + RF.add(node); + + for (V tn : gds.getTargetNodes(node).distinctValues()) { + int top_id = index2topsort.get(node2index.get(tn)); + + if (top_id == upper_bound) + System.out.println("!!!Cycle detected!!!"); + else if (!node2mark.get(tn) && top_id < upper_bound) + dfsForward(tn); + } + } + + @Override + public void edgeDeleted(V source, V target) { + // Edge deletion does not affect topsort + } + + @Override + public void nodeInserted(V n) { + node2mark.put(n, false); + node2index.put(n, index); + index2node.put(index, n); + index2topsort.put(index, index); + topsort2index.put(index, index); + index++; + } + + @Override + public void nodeDeleted(V n) { + node2mark.remove(n); + int node_id = node2index.remove(n); + index2node.remove(node_id); + int top_id = index2topsort.remove(node_id); + topsort2index.remove(top_id); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java new file mode 100644 index 00000000..f6ef9847 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.misc.scc; + +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Stack; + +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; + +/** + * Efficient algorithms to compute the Strongly Connected Components in a directed graph. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the nodes in the graph + */ +public class SCC { + + private SCC() {/*Utility class constructor*/} + + public static long sccId = 0; + + /** + * Computes the SCCs for the given graph and returns them as a multiset. (Iterative version of Tarjan's algorithm) + * + * @param g + * the directed graph data source + * @return the set of SCCs + */ + public static SCCResult computeSCC(IGraphDataSource g) { + int index = 0; + Set> ret = new HashSet>(); + + // stores the lowlink and index information for the given node + Map nodeMap = CollectionsFactory.createMap(); + + // stores all target nodes of a given node - the list will be modified + Map> targetNodeMap = CollectionsFactory.createMap(); + + // stores those target nodes for a given node which have not been visited + Map> notVisitedMap = CollectionsFactory.createMap(); + + // stores the nodes during the traversal + Stack nodeStack = new Stack(); + + // stores the nodes which belong to an scc (there can be many sccs in the stack at the same time) + Stack sccStack = new Stack(); + + boolean sink = false, finishedTraversal = true; + + // initialize all nodes with 0 index and 0 lowlink + Set allNodes = g.getAllNodes(); + for (V n : allNodes) { + nodeMap.put(n, new SCCProperty(0, 0)); + } + + for (V n : allNodes) { + // if the node has not been visited yet + if (nodeMap.get(n).getIndex() == 0) { + nodeStack.push(n); + + while (!nodeStack.isEmpty()) { + V currentNode = nodeStack.peek(); + sink = false; + finishedTraversal = false; + SCCProperty prop = nodeMap.get(currentNode); + + if (nodeMap.get(currentNode).getIndex() == 0) { + index++; + sccStack.push(currentNode); + prop.setIndex(index); + prop.setLowlink(index); + + notVisitedMap.put(currentNode, new HashSet()); + + // storing the target nodes of the actual node + if (g.getTargetNodes(currentNode) != null) { + Set targets = g.getTargetNodes(currentNode).distinctValues(); + targetNodeMap.put(currentNode, CollectionsFactory.createSet(targets)); + } + } + + if (targetNodeMap.get(currentNode) != null) { + + // remove node from stack, the exploration of its children has finished + if (targetNodeMap.get(currentNode).size() == 0) { + targetNodeMap.remove(currentNode); + + nodeStack.pop(); + + for (V targetNode : g.getTargetNodes(currentNode).distinctValues()) { + if (notVisitedMap.get(currentNode).contains(targetNode)) { + prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getLowlink())); + } else if (sccStack.contains(targetNode)) { + prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getIndex())); + } + } + + finishedTraversal = true; + } else { + V targetNode = targetNodeMap.get(currentNode).iterator().next(); + targetNodeMap.get(currentNode).remove(targetNode); + // if the targetNode has not yet been visited push it to the stack + // and mark it in the notVisitedMap + if (nodeMap.get(targetNode).getIndex() == 0) { + notVisitedMap.get(currentNode).add(targetNode); + nodeStack.add(targetNode); + } + } + } + // if currentNode has no target nodes + else { + nodeStack.pop(); + sink = true; + } + + // create scc if node is a sink or an scc has been found + if ((sink || finishedTraversal) && (prop.getLowlink() == prop.getIndex())) { + Set sc = new HashSet(); + V targetNode = null; + + do { + targetNode = sccStack.pop(); + sc.add(targetNode); + } while (!targetNode.equals(currentNode)); + + ret.add(sc); + } + } + } + } + + return new SCCResult(ret, g); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCProperty.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCProperty.java new file mode 100644 index 00000000..51ee834e --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCProperty.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.misc.scc; + +public class SCCProperty { + private int index; + private int lowlink; + + public SCCProperty(int index, int lowlink) { + super(); + this.index = index; + this.lowlink = lowlink; + } + + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + public int getLowlink() { + return lowlink; + } + + public void setLowlink(int lowlink) { + this.lowlink = lowlink; + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCResult.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCResult.java new file mode 100644 index 00000000..2e511fd6 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCCResult.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.misc.scc; + +import java.util.Map.Entry; +import java.util.Set; + +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; + +public class SCCResult { + + private Set> sccs; + private IGraphDataSource gds; + + public SCCResult(Set> sccs, IGraphDataSource gds) { + this.sccs = sccs; + this.gds = gds; + } + + public Set> getSccs() { + return sccs; + } + + public int getSCCCount() { + return sccs.size(); + } + + public double getAverageNodeCount() { + double a = 0; + + for (Set s : sccs) { + a += s.size(); + } + + return a / sccs.size(); + } + + public double getAverageEdgeCount() { + long edgeSum = 0; + + for (Set scc : sccs) { + for (V source : scc) { + for (Entry entry : gds.getTargetNodes(source).entriesWithMultiplicities()) { + if (scc.contains(entry.getKey())) { + edgeSum += entry.getValue(); + } + } + } + } + + return (double) edgeSum / (double) sccs.size(); + } + + public int getBiggestSCCSize() { + int max = 0; + + for (Set scc : sccs) { + if (scc.size() > max) + max = scc.size(); + } + + return max; + } + + public long getSumOfSquares() { + long sum = 0; + + for (Set scc : sccs) { + sum += scc.size() * scc.size(); + } + + return sum; + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java new file mode 100644 index 00000000..db46d178 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.alg.misc.topsort; + +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; + +import java.util.*; + +/** + * @since 1.6 + */ +public class TopologicalSorting { + + private TopologicalSorting() {/*Utility class constructor*/} + + private static final class Pair { + public T element; + public boolean isParent; + + public Pair(final T element, final boolean isParent) { + this.element = element; + this.isParent = isParent; + } + } + + /** + * Returns a topological ordering for the given graph data source. + * Output format: if there is an a -> b (transitive) reachability, then node a will come before node b in the resulting list. + * + * @param gds the graph data source + * @return a topological ordering + */ + public static List compute(final IGraphDataSource gds) { + final Set visited = new HashSet(); + final LinkedList result = new LinkedList(); + final Stack> dfsStack = new Stack>(); + + for (final T node : gds.getAllNodes()) { + if (!visited.contains(node)) { + dfsStack.push(new Pair(node, false)); + } + + while (!dfsStack.isEmpty()) { + final Pair head = dfsStack.pop(); + final T source = head.element; + + if (head.isParent) { + // we have already seen source, push it to the resulting stack + result.addFirst(source); + } else { + // first time we see source, continue with its children + visited.add(source); + dfsStack.push(new Pair(source, true)); + + for (final T target : gds.getTargetNodes(source).distinctValues()) { + if (!visited.contains(target)) { + dfsStack.push(new Pair(target, false)); + } + } + } + } + } + + return result; + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java new file mode 100644 index 00000000..5343f956 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java @@ -0,0 +1,140 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.rete.itc.alg.representative; + +import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphObserver; +import tools.refinery.viatra.runtime.matchers.util.Direction; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +public abstract class RepresentativeElectionAlgorithm implements IGraphObserver { + protected final Graph graph; + protected final Map representatives = new HashMap<>(); + protected final Map> components = new HashMap<>(); + private RepresentativeObserver observer; + + protected RepresentativeElectionAlgorithm(Graph graph) { + this.graph = graph; + initializeComponents(); + graph.attachObserver(this); + } + + protected abstract void initializeComponents(); + + protected void initializeSet(Set set) { + var iterator = set.iterator(); + if (!iterator.hasNext()) { + // Set is empty. + return; + } + var representative = iterator.next(); + for (var node : set) { + var oldRepresentative = representatives.put(node, representative); + if (oldRepresentative != null && !representative.equals(oldRepresentative)) { + throw new IllegalStateException("Node %s is already in a set represented by %s, cannot add it to %s" + .formatted(node, oldRepresentative, set)); + } + } + components.put(representative, set); + } + + protected void merge(Object leftRepresentative, Object rightRepresentative) { + if (leftRepresentative.equals(rightRepresentative)) { + return; + } + var leftSet = getComponent(leftRepresentative); + var rightSet = getComponent(rightRepresentative); + if (leftSet.size() < rightSet.size()) { + merge(rightRepresentative, rightSet, leftRepresentative, leftSet); + } else { + merge(leftRepresentative, leftSet, rightRepresentative, rightSet); + } + } + + private void merge(Object preservedRepresentative, Set preservedSet, Object removedRepresentative, + Set removedSet) { + components.remove(removedRepresentative); + for (var node : removedSet) { + representatives.put(node, preservedRepresentative); + preservedSet.add(node); + notifyToObservers(node, removedRepresentative, preservedRepresentative); + } + } + + protected void assignNewRepresentative(Object oldRepresentative, Set set) { + var iterator = set.iterator(); + if (!iterator.hasNext()) { + return; + } + var newRepresentative = iterator.next(); + components.put(newRepresentative, set); + for (var node : set) { + var oldRepresentativeOfNode = representatives.put(node, newRepresentative); + if (!oldRepresentative.equals(oldRepresentativeOfNode)) { + throw new IllegalArgumentException("Node %s was not represented by %s but by %s" + .formatted(node, oldRepresentative, oldRepresentativeOfNode)); + } + notifyToObservers(node, oldRepresentative, newRepresentative); + } + } + + public void setObserver(RepresentativeObserver observer) { + this.observer = observer; + } + + public Map> getComponents() { + return components; + } + + public Object getRepresentative(Object node) { + return representatives.get(node); + } + + public Set getComponent(Object representative) { + return components.get(representative); + } + + public void dispose() { + graph.detachObserver(this); + } + + @Override + public void nodeInserted(Object n) { + var component = new HashSet<>(1); + component.add(n); + initializeSet(component); + notifyToObservers(n, n, Direction.INSERT); + } + + @Override + public void nodeDeleted(Object n) { + var representative = representatives.remove(n); + if (!representative.equals(n)) { + throw new IllegalStateException("Trying to delete node with dangling edges"); + } + components.remove(representative); + notifyToObservers(n, representative, Direction.DELETE); + } + + protected void notifyToObservers(Object node, Object oldRepresentative, Object newRepresentative) { + notifyToObservers(node, oldRepresentative, Direction.DELETE); + notifyToObservers(node, newRepresentative, Direction.INSERT); + } + + protected void notifyToObservers(Object node, Object representative, Direction direction) { + if (observer != null) { + observer.tupleChanged(node, representative, direction); + } + } + + public interface Factory { + RepresentativeElectionAlgorithm create(Graph graph); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeObserver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeObserver.java new file mode 100644 index 00000000..6b772fa8 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeObserver.java @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.rete.itc.alg.representative; + +import tools.refinery.viatra.runtime.matchers.util.Direction; + +public interface RepresentativeObserver { + void tupleChanged(Object node, Object representative, Direction direction); +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java new file mode 100644 index 00000000..4acb2b77 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.rete.itc.alg.representative; + +import tools.refinery.viatra.runtime.rete.itc.alg.misc.GraphHelper; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.bfs.BFS; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCC; +import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; + +import java.util.Collection; +import java.util.Set; + +public class StronglyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { + public StronglyConnectedComponentAlgorithm(Graph graph) { + super(graph); + } + + @Override + protected void initializeComponents() { + var computedSCCs = SCC.computeSCC(graph).getSccs(); + for (var computedSCC : computedSCCs) { + initializeSet(computedSCC); + } + } + + @Override + public void edgeInserted(Object source, Object target) { + var sourceRoot = getRepresentative(source); + var targetRoot = getRepresentative(target); + if (sourceRoot.equals(targetRoot)) { + // New edge does not change strongly connected components. + return; + } + if (BFS.isReachable(target, source, graph)) { + merge(sourceRoot, targetRoot); + } + } + + @Override + public void edgeDeleted(Object source, Object target) { + var sourceRoot = getRepresentative(source); + var targetRoot = getRepresentative(target); + if (!sourceRoot.equals(targetRoot)) { + // New edge does not change strongly connected components. + return; + } + var component = GraphHelper.getSubGraph(getComponent(sourceRoot), graph); + if (!BFS.isReachable(source, target, component)) { + var newSCCs = SCC.computeSCC(component).getSccs(); + split(sourceRoot, newSCCs); + } + } + + private void split(Object preservedRepresentative, Collection> sets) { + for (var set : sets) { + if (set.contains(preservedRepresentative)) { + components.put(preservedRepresentative, set); + } else { + assignNewRepresentative(preservedRepresentative, set); + } + } + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java new file mode 100644 index 00000000..704f0235 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java @@ -0,0 +1,85 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime.rete.itc.alg.representative; + +import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; + +import java.util.ArrayDeque; +import java.util.HashSet; +import java.util.Set; + +public class WeaklyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { + public WeaklyConnectedComponentAlgorithm(Graph graph) { + super(graph); + } + + @Override + protected void initializeComponents() { + for (var node : graph.getAllNodes()) { + if (representatives.containsKey(node)) { + continue; + } + var reachable = getReachableNodes(node); + initializeSet(reachable); + } + } + + @Override + public void edgeInserted(Object source, Object target) { + var sourceRoot = getRepresentative(source); + var targetRoot = getRepresentative(target); + merge(sourceRoot, targetRoot); + } + + @Override + public void edgeDeleted(Object source, Object target) { + var sourceRoot = getRepresentative(source); + var targetRoot = getRepresentative(target); + if (!sourceRoot.equals(targetRoot)) { + throw new IllegalArgumentException("Trying to remove edge not in graph"); + } + var targetReachable = getReachableNodes(target); + if (!targetReachable.contains(source)) { + split(sourceRoot, targetReachable); + } + } + + private void split(Object sourceRepresentative, Set targetReachable) { + var sourceComponent = getComponent(sourceRepresentative); + sourceComponent.removeAll(targetReachable); + if (targetReachable.contains(sourceRepresentative)) { + components.put(sourceRepresentative, targetReachable); + assignNewRepresentative(sourceRepresentative, sourceComponent); + } else { + assignNewRepresentative(sourceRepresentative, targetReachable); + } + } + + private Set getReachableNodes(Object source) { + var retSet = new HashSet<>(); + retSet.add(source); + var nodeQueue = new ArrayDeque<>(); + nodeQueue.addLast(source); + + while (!nodeQueue.isEmpty()) { + var node = nodeQueue.removeFirst(); + for (var neighbor : graph.getTargetNodes(node).distinctValues()) { + if (!retSet.contains(neighbor)) { + retSet.add(neighbor); + nodeQueue.addLast(neighbor); + } + } + for (var neighbor : graph.getSourceNodes(node).distinctValues()) { + if (!retSet.contains(neighbor)) { + retSet.add(neighbor); + nodeQueue.addLast(neighbor); + } + } + } + + return retSet; + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/util/CollectionHelper.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/util/CollectionHelper.java new file mode 100644 index 00000000..6655be6d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/util/CollectionHelper.java @@ -0,0 +1,64 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.itc.alg.util; + +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; + +import java.util.Set; + +/** + * @author Tamas Szabo + * + */ +public class CollectionHelper { + + private CollectionHelper() {/*Utility class constructor*/} + + /** + * Returns the intersection of two sets. It calls {@link Set#retainAll(java.util.Collection)} but returns a new set + * containing the elements of the intersection. + * + * @param set1 + * the first set (can be null, interpreted as empty) + * @param set2 + * the second set (can be null, interpreted as empty) + * @return the intersection of the sets + * @since 1.7 + */ + public static Set intersection(Set set1, Set set2) { + if (set1 == null || set2 == null) + return CollectionsFactory.createSet(); + + Set intersection = CollectionsFactory.createSet(set1); + intersection.retainAll(set2); + return intersection; + } + + + /** + * Returns the difference of two sets (S1\S2). It calls {@link Set#removeAll(java.util.Collection)} but returns a + * new set containing the elements of the difference. + * + * @param set1 + * the first set (can be null, interpreted as empty) + * @param set2 + * the second set (can be null, interpreted as empty) + * @return the difference of the sets + * @since 1.7 + */ + public static Set difference(Set set1, Set set2) { + if (set1 == null) + return CollectionsFactory.createSet(); + + Set difference = CollectionsFactory.createSet(set1); + if (set2 != null) difference.removeAll(set2); + return difference; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/DotGenerator.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/DotGenerator.java new file mode 100644 index 00000000..f7f6b5ed --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/DotGenerator.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package tools.refinery.viatra.runtime.rete.itc.graphimpl; + +import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCC; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.scc.SCCResult; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.function.Function; + +/** + * This class contains utility methods to generate dot representations for {@link Graph} instances. + * + * @author Tamas Szabo + * @since 2.3 + */ +public class DotGenerator { + + private static final String[] colors = new String[] { "yellow", "blue", "red", "green", "gray", "cyan" }; + + private DotGenerator() { + + } + + /** + * Generates the dot representation for the given graph. + * + * @param graph + * the graph + * @param colorSCCs + * specifies if the strongly connected components with size greater than shall be colored + * @param nameFunction + * use this function to provide custom names to nodes, null if the default toString shall be used + * @param colorFunction + * use this function to provide custom color to nodes, null if the default white color shall be used + * @param edgeFunction + * use this function to provide custom edge labels, null if no edge label shall be printed + * @return the dot representation as a string + */ + public static String generateDot(final Graph graph, final boolean colorSCCs, + final Function nameFunction, final Function colorFunction, + final Function> edgeFunction) { + final Map colorMap = new HashMap(); + + if (colorSCCs) { + final SCCResult result = SCC.computeSCC(graph); + final Set> sccs = result.getSccs(); + + int i = 0; + for (final Set scc : sccs) { + if (scc.size() > 1) { + for (final V node : scc) { + final String color = colorMap.get(node); + if (color == null) { + colorMap.put(node, colors[i % colors.length]); + } else { + colorMap.put(node, colorMap.get(node) + ":" + colors[i % colors.length]); + } + } + i++; + } + } + + // if a node has no color yet, then make it white + for (final V node : graph.getAllNodes()) { + if (!colorMap.containsKey(node)) { + colorMap.put(node, "white"); + } + } + } else { + for (final V node : graph.getAllNodes()) { + colorMap.put(node, "white"); + } + } + + if (colorFunction != null) { + for (final V node : graph.getAllNodes()) { + colorMap.put(node, colorFunction.apply(node)); + } + } + + final StringBuilder builder = new StringBuilder(); + builder.append("digraph g {\n"); + + for (final V node : graph.getAllNodes()) { + final String nodePresentation = nameFunction == null ? node.toString() : nameFunction.apply(node); + builder.append("\"" + nodePresentation + "\""); + builder.append("[style=filled,fillcolor=" + colorMap.get(node) + "]"); + builder.append(";\n"); + } + + for (final V source : graph.getAllNodes()) { + final IMemoryView targets = graph.getTargetNodes(source); + if (!targets.isEmpty()) { + final String sourcePresentation = nameFunction == null ? source.toString() : nameFunction.apply(source); + for (final V target : targets.distinctValues()) { + String edgeLabel = null; + if (edgeFunction != null) { + final Function v1 = edgeFunction.apply(source); + if (v1 != null) { + edgeLabel = v1.apply(target); + } + } + + final String targetPresentation = nameFunction == null ? target.toString() + : nameFunction.apply(target); + + builder.append("\"" + sourcePresentation + "\" -> \"" + targetPresentation + "\""); + if (edgeLabel != null) { + builder.append("[label=\"" + edgeLabel + "\"]"); + } + builder.append(";\n"); + } + } + } + + builder.append("}"); + return builder.toString(); + } + + /** + * Generates the dot representation for the given graph. No special pretty printing customization will be applied. + * + * @param graph + * the graph + * @return the dot representation as a string + */ + public static String generateDot(final Graph graph) { + return generateDot(graph, false, null, null, null); + } + + /** + * Returns a simple name shortener function that can be used in the graphviz visualization to help with readability. + * WARNING: if you shorten the name of the {@link Node}s too much, the visualization may become incorrect because + * grahpviz will treat different nodes as the same if their shortened names are the same. + * + * @param maxLength + * the maximum length of the text that is kept from the toString of the objects in the graph + * @return the shrunk toString value + */ + public static Function getNameShortener(final int maxLength) { + return new Function() { + @Override + public String apply(final V obj) { + final String value = obj.toString(); + return value.substring(0, Math.min(value.length(), maxLength)); + } + }; + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/Graph.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/Graph.java new file mode 100644 index 00000000..91604cb2 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/graphimpl/Graph.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.graphimpl; + +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphObserver; +import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; + +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +public class Graph implements IGraphDataSource, IBiDirectionalGraphDataSource { + + // source -> target -> count + private IMultiLookup outgoingEdges; + // target -> source -> count + private IMultiLookup incomingEdges; + + private Set nodes; + + private List> observers; + + public Graph() { + outgoingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); + incomingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); + nodes = CollectionsFactory.createSet(); + observers = CollectionsFactory.createObserverList(); + } + + public void insertEdge(V source, V target) { + outgoingEdges.addPair(source, target); + incomingEdges.addPair(target, source); + + for (IGraphObserver go : observers) { + go.edgeInserted(source, target); + } + } + + /** + * No-op if trying to delete edge that does not exist + * + * @since 2.0 + * @see #deleteEdgeIfExists(Object, Object) + */ + public void deleteEdgeIfExists(V source, V target) { + boolean containedEdge = outgoingEdges.lookupOrEmpty(source).containsNonZero(target); + if (containedEdge) { + deleteEdgeThatExists(source, target); + } + } + + /** + * @throws IllegalStateException + * if trying to delete edge that does not exist + * @since 2.0 + * @see #deleteEdgeIfExists(Object, Object) + */ + public void deleteEdgeThatExists(V source, V target) { + outgoingEdges.removePair(source, target); + incomingEdges.removePair(target, source); + for (IGraphObserver go : observers) { + go.edgeDeleted(source, target); + } + } + + /** + * @deprecated use explicitly {@link #deleteEdgeThatExists(Object, Object)} or + * {@link #deleteEdgeIfExists(Object, Object)} instead. To preserve backwards compatibility, this method + * delegates to the latter. + * + */ + @Deprecated + public void deleteEdge(V source, V target) { + deleteEdgeIfExists(source, target); + } + + /** + * Insert the given node into the graph. + */ + public void insertNode(V node) { + if (nodes.add(node)) { + for (IGraphObserver go : observers) { + go.nodeInserted(node); + } + } + } + + /** + * Deletes the given node AND all of the edges going in and out from the node. + */ + public void deleteNode(V node) { + if (nodes.remove(node)) { + IMemoryView incomingView = incomingEdges.lookup(node); + if (incomingView != null) { + Map incoming = CollectionsFactory.createMap(incomingView.asMap()); + + for (Entry entry : incoming.entrySet()) { + for (int i = 0; i < entry.getValue(); i++) { + deleteEdgeThatExists(entry.getKey(), node); + } + } + } + + IMemoryView outgoingView = outgoingEdges.lookup(node); + if (outgoingView != null) { + Map outgoing = CollectionsFactory.createMap(outgoingView.asMap()); + + for (Entry entry : outgoing.entrySet()) { + for (int i = 0; i < entry.getValue(); i++) { + deleteEdgeThatExists(node, entry.getKey()); + } + } + } + + for (IGraphObserver go : observers) { + go.nodeDeleted(node); + } + } + } + + @Override + public void attachObserver(IGraphObserver go) { + observers.add(go); + } + + @Override + public void attachAsFirstObserver(IGraphObserver observer) { + observers.add(0, observer); + } + + @Override + public void detachObserver(IGraphObserver go) { + observers.remove(go); + } + + @Override + public Set getAllNodes() { + return nodes; + } + + @Override + public IMemoryView getTargetNodes(V source) { + return outgoingEdges.lookupOrEmpty(source); + } + + @Override + public IMemoryView getSourceNodes(V target) { + return incomingEdges.lookupOrEmpty(target); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("nodes = "); + for (V n : getAllNodes()) { + sb.append(n.toString()); + sb.append(" "); + } + sb.append(" edges = "); + for (V source : outgoingEdges.distinctKeys()) { + IMemoryView targets = outgoingEdges.lookup(source); + for (V target : targets.distinctValues()) { + int count = targets.getCount(target); + for (int i = 0; i < count; i++) { + sb.append("(" + source + "," + target + ") "); + } + } + } + return sb.toString(); + } + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalGraphDataSource.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalGraphDataSource.java new file mode 100644 index 00000000..4fcaa71f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalGraphDataSource.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.igraph; + +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiset; + +/** + * A bi-directional graph data source supports all operations that an {@link IGraphDataSource} does, but it + * also makes it possible to query the incoming edges of nodes, not only the outgoing edges. + * + * @author Tamas Szabo + * + * @param the type of the nodes in the graph + */ +public interface IBiDirectionalGraphDataSource extends IGraphDataSource { + + /** + * Returns the source nodes for the given target node. + * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source. + * + * The method must not return null. + * + * @param target the target node + * @return the multiset of source nodes + * @since 2.0 + */ + public IMemoryView getSourceNodes(V target); + +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalWrapper.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalWrapper.java new file mode 100644 index 00000000..c4315ca2 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IBiDirectionalWrapper.java @@ -0,0 +1,110 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.igraph; + +import java.util.Set; + +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; + +/** + * This class can be used to wrap an {@link IGraphDataSource} into an {@link IBiDirectionalGraphDataSource}. This class + * provides support for the retrieval of source nodes for a given target which is not supported by standard + * {@link IGraphDataSource} implementations. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the nodes in the graph data source + */ +public class IBiDirectionalWrapper implements IBiDirectionalGraphDataSource, IGraphObserver { + + private IGraphDataSource wrappedDataSource; + // target -> source -> count + private IMultiLookup incomingEdges; + + public IBiDirectionalWrapper(IGraphDataSource gds) { + this.wrappedDataSource = gds; + + this.incomingEdges = CollectionsFactory.createMultiLookup( + Object.class, MemoryType.MULTISETS, Object.class); + + if (gds.getAllNodes() != null) { + for (V source : gds.getAllNodes()) { + IMemoryView targets = gds.getTargetNodes(source); + for (V target : targets.distinctValues()) { + int count = targets.getCount(target); + for (int i = 0; i < count; i++) { + edgeInserted(source, target); + } + } + } + } + + gds.attachAsFirstObserver(this); + } + + @Override + public void attachObserver(IGraphObserver observer) { + wrappedDataSource.attachObserver(observer); + } + + @Override + public void attachAsFirstObserver(IGraphObserver observer) { + wrappedDataSource.attachAsFirstObserver(observer); + } + + @Override + public void detachObserver(IGraphObserver observer) { + wrappedDataSource.detachObserver(observer); + } + + @Override + public Set getAllNodes() { + return wrappedDataSource.getAllNodes(); + } + + @Override + public IMemoryView getTargetNodes(V source) { + return wrappedDataSource.getTargetNodes(source); + } + + @Override + public IMemoryView getSourceNodes(V target) { + return incomingEdges.lookupOrEmpty(target); + } + + @Override + public void edgeInserted(V source, V target) { + incomingEdges.addPair(target, source); + } + + @Override + public void edgeDeleted(V source, V target) { + incomingEdges.removePair(target, source); + } + + @Override + public void nodeInserted(V n) { + + } + + @Override + public void nodeDeleted(V node) { + + } + + @Override + public String toString() { + return wrappedDataSource.toString(); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphDataSource.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphDataSource.java new file mode 100644 index 00000000..9159a692 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphDataSource.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.igraph; + +import tools.refinery.viatra.runtime.matchers.util.IMemoryView; +import tools.refinery.viatra.runtime.matchers.util.IMultiset; + +import java.util.Set; + +/** + * The interface prescribes the set of operations that a graph data source must support. + *

Note that the old version of the interface is broken at version 1.6; + * MultiSets are now presented as Maps instead of Lists. + * + * @author Tamas Szabo + * + * @param + * the type of the nodes in the graph + */ +public interface IGraphDataSource { + + /** + * Attaches a new graph observer to this graph data source. Observers will be notified in the order they have been registered. + * + * @param observer the graph observer + */ + public void attachObserver(IGraphObserver observer); + + /** + * Attaches a new graph observer to this graph data source as the first one. + * In the notification order this observer will be the first one as long as another call to this method happens. + * + * @param observer the graph observer + * @since 1.6 + */ + public void attachAsFirstObserver(IGraphObserver observer); + + /** + * Detaches an already registered graph observer from this graph data source. + * + * @param observer the graph observer + */ + public void detachObserver(IGraphObserver observer); + + /** + * Returns the complete set of nodes in the graph data source. + * + * @return the set of all nodes + */ + public Set getAllNodes(); + + /** + * Returns the target nodes for the given source node. + * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source. + * + * The method must not return null. + * + * @param source the source node + * @return the multiset of target nodes + * @since 2.0 + */ + public IMemoryView getTargetNodes(V source); +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphObserver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphObserver.java new file mode 100644 index 00000000..a282216d --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/IGraphObserver.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.igraph; + +/** + * Interface GraphObserver is used to observ the changes in a graph; edge and node insertion/deleteion. + * + * @author Tamas Szabo + * + */ +public interface IGraphObserver { + + /** + * Used to notify when an edge is inserted into the graph. + * + * @param source + * the source of the edge + * @param target + * the target of the edge + */ + public void edgeInserted(V source, V target); + + /** + * Used to notify when an edge is deleted from the graph. + * + * @param source + * the source of the edge + * @param target + * the target of the edge + */ + public void edgeDeleted(V source, V target); + + /** + * Used to notify when a node is inserted into the graph. + * + * @param n + * the node + */ + public void nodeInserted(V n); + + /** + * Used to notify when a node is deleted from the graph. + * + * @param n + * the node + */ + public void nodeDeleted(V n); +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcDataSource.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcDataSource.java new file mode 100644 index 00000000..5ede600f --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcDataSource.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.igraph; + +import java.util.Set; + +import tools.refinery.viatra.runtime.rete.itc.alg.misc.IGraphPathFinder; + +/** + * This interface defines those methods that a transitive reachability data source should provide. + * + * @author Tamas Szabo + * + * @param + * the type parameter of the node + */ +public interface ITcDataSource { + + /** + * Attach a transitive closure relation observer. + * + * @param to + * the observer object + */ + public void attachObserver(ITcObserver to); + + /** + * Detach a transitive closure relation observer. + * + * @param to + * the observer object + */ + public void detachObserver(ITcObserver to); + + /** + * Returns all nodes which are reachable from the source node. + * + * @param source + * the source node + * @return the set of target nodes + */ + public Set getAllReachableTargets(V source); + + /** + * Returns all nodes from which the target node is reachable. + * + * @param target + * the target node + * @return the set of source nodes + */ + public Set getAllReachableSources(V target); + + /** + * Returns true if the target node is reachable from the source node. + * + * @param source + * the source node + * @param target + * the target node + * @return true if target is reachable from source, false otherwise + */ + public boolean isReachable(V source, V target); + + /** + * The returned {@link IGraphPathFinder} can be used to retrieve paths between nodes using transitive reachability. + * + * @return a path finder for the graph. + */ + public IGraphPathFinder getPathFinder(); + + /** + * Call this method to properly dispose the data structures of a transitive closure algorithm. + */ + public void dispose(); +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcObserver.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcObserver.java new file mode 100644 index 00000000..74e0cb75 --- /dev/null +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/igraph/ITcObserver.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v. 2.0 which is available at + * http://www.eclipse.org/legal/epl-v20.html. + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ + +package tools.refinery.viatra.runtime.rete.itc.igraph; + +/** + * Interface ITcObserver is used to observe the changes in a transitive closure relation; tuple insertion/deletion. + * + * @author Szabo Tamas + * + */ +public interface ITcObserver { + + /** + * Used to notify when a tuple is inserted into the transitive closure relation. + * + * @param source + * the source of the tuple + * @param target + * the target of the tuple + */ + public void tupleInserted(V source, V target); + + /** + * Used to notify when a tuple is deleted from the transitive closure relation. + * + * @param source + * the source of the tuple + * @param target + * the target of the tuple + */ + public void tupleDeleted(V source, V target); +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java index 231bdfe5..3e4ea4e0 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/NodeFactory.java @@ -11,9 +11,9 @@ package tools.refinery.viatra.runtime.rete.network; import org.apache.log4j.Logger; import org.eclipse.emf.common.util.EMap; -import tools.refinery.viatra.runtime.base.itc.alg.representative.RepresentativeElectionAlgorithm; -import tools.refinery.viatra.runtime.base.itc.alg.representative.StronglyConnectedComponentAlgorithm; -import tools.refinery.viatra.runtime.base.itc.alg.representative.WeaklyConnectedComponentAlgorithm; +import tools.refinery.viatra.runtime.rete.itc.alg.representative.RepresentativeElectionAlgorithm; +import tools.refinery.viatra.runtime.rete.itc.alg.representative.StronglyConnectedComponentAlgorithm; +import tools.refinery.viatra.runtime.rete.itc.alg.representative.WeaklyConnectedComponentAlgorithm; import tools.refinery.viatra.runtime.matchers.context.IPosetComparator; import tools.refinery.viatra.runtime.matchers.psystem.IExpressionEvaluator; import tools.refinery.viatra.runtime.matchers.psystem.IRelationEvaluator; diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java index 8435a547..d244e644 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/CommunicationTracker.java @@ -3,7 +3,7 @@ * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. - * + * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package tools.refinery.viatra.runtime.rete.network.communication; @@ -16,9 +16,9 @@ import java.util.PriorityQueue; import java.util.Queue; import java.util.Set; -import tools.refinery.viatra.runtime.base.itc.alg.incscc.IncSCCAlg; -import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting; -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.rete.itc.alg.incscc.IncSCCAlg; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.topsort.TopologicalSorting; +import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; import tools.refinery.viatra.runtime.rete.aggregation.IAggregatorNode; import tools.refinery.viatra.runtime.rete.boundary.ExternalInputEnumeratorNode; @@ -52,7 +52,7 @@ import tools.refinery.viatra.runtime.rete.single.TrimmerNode; * precisely, the mailboxes that contain the messages) for the associated {@link ReteContainer}. The ordering is * governed by the strongly connected components in the dependency network and follows a topological sorting scheme; * those mailboxes will be emptied first whose owner nodes do not depend on other undelivered messages. - * + * * @author Tamas Szabo * @since 1.6 * @@ -186,7 +186,7 @@ public abstract class CommunicationTracker { // or true trimming in its sole parent directParents.size() == 1 && trueTrimming(directParents.iterator().next())))) && // disallow fallthrough: external updates should be stored (if updates are delayed) - (!(node instanceof ExternalInputEnumeratorNode)) && + (!(node instanceof ExternalInputEnumeratorNode)) && // disallow fallthrough: RelationEvaluatorNode needs to be notified in batch-style, and the batching is done by the mailbox // however, it is not the RelationEvaluatorNode itself that is interesting here, as that indirectly uses the BatchingReceiver // so we need to disable fall-through for the BatchingReceiver @@ -375,7 +375,7 @@ public abstract class CommunicationTracker { /** * Unregisters a dependency between source and target. - * + * * @param source * the source node * @param target @@ -416,10 +416,10 @@ public abstract class CommunicationTracker { } /** - * Returns true if the given source-target edge in the communication network acts as a recursion cut point. + * Returns true if the given source-target edge in the communication network acts as a recursion cut point. * The current implementation considers edges leading into {@link ProductionNode}s as cut point iff - * both source and target belong to the same group. - * + * both source and target belong to the same group. + * * @param source the source node * @param target the target node * @return true if the edge is a cut point, false otherwise diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java index 1ff69882..79179880 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/communication/timely/TimelyCommunicationTracker.java @@ -3,7 +3,7 @@ * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. - * + * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package tools.refinery.viatra.runtime.rete.network.communication.timely; @@ -15,8 +15,8 @@ import java.util.Map.Entry; import java.util.Set; import java.util.function.Function; -import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting; -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.topsort.TopologicalSorting; +import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; import tools.refinery.viatra.runtime.rete.index.IndexerListener; import tools.refinery.viatra.runtime.rete.index.SpecializedProjectionIndexer; @@ -37,7 +37,7 @@ import tools.refinery.viatra.runtime.rete.single.DiscriminatorDispatcherNode; /** * Timely (DDF) implementation of the {@link CommunicationTracker}. - * + * * @author Tamas Szabo * @since 2.3 */ diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java index 6a1e305c..95018c4f 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/RepresentativeElectionNode.java @@ -8,9 +8,9 @@ *******************************************************************************/ package tools.refinery.viatra.runtime.rete.single; -import tools.refinery.viatra.runtime.base.itc.alg.representative.RepresentativeElectionAlgorithm; -import tools.refinery.viatra.runtime.base.itc.alg.representative.RepresentativeObserver; -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.rete.itc.alg.representative.RepresentativeElectionAlgorithm; +import tools.refinery.viatra.runtime.rete.itc.alg.representative.RepresentativeObserver; +import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; import tools.refinery.viatra.runtime.matchers.tuple.Tuple; import tools.refinery.viatra.runtime.matchers.tuple.Tuples; import tools.refinery.viatra.runtime.matchers.util.Clearable; diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java index eeead31b..fdda4ef4 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/single/TransitiveClosureNode.java @@ -8,11 +8,11 @@ *******************************************************************************/ package tools.refinery.viatra.runtime.rete.single; -import tools.refinery.viatra.runtime.base.itc.alg.incscc.IncSCCAlg; -import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple; -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; +import tools.refinery.viatra.runtime.rete.itc.alg.incscc.IncSCCAlg; +import tools.refinery.viatra.runtime.rete.itc.alg.misc.Tuple; +import tools.refinery.viatra.runtime.rete.itc.graphimpl.Graph; +import tools.refinery.viatra.runtime.rete.itc.igraph.ITcDataSource; +import tools.refinery.viatra.runtime.rete.itc.igraph.ITcObserver; import tools.refinery.viatra.runtime.matchers.tuple.Tuples; import tools.refinery.viatra.runtime.matchers.util.Clearable; import tools.refinery.viatra.runtime.matchers.util.Direction; diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java deleted file mode 100644 index d0367cde..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingAlg.java +++ /dev/null @@ -1,226 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.counting; - -import java.util.List; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -/** - * This class is the optimized implementation of the Counting algorithm. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the nodes in the graph data source - */ -public class CountingAlg implements IGraphObserver, ITcDataSource { - - private CountingTcRelation tc = null; - private IBiDirectionalGraphDataSource gds = null; - private List> observers; - - /** - * Constructs a new Counting algorithm and initializes the transitive closure relation with the given graph data - * source. Attach itself on the graph data source as an observer. - * - * @param gds - * the graph data source instance - */ - public CountingAlg(IGraphDataSource gds) { - - if (gds instanceof IBiDirectionalGraphDataSource) { - this.gds = (IBiDirectionalGraphDataSource) gds; - } else { - this.gds = new IBiDirectionalWrapper(gds); - } - - observers = CollectionsFactory.>createObserverList(); - tc = new CountingTcRelation(true); - - initTc(); - gds.attachObserver(this); - } - - /** - * Initializes the transitive closure relation. - */ - private void initTc() { - this.setTcRelation(CountingTcRelation.createFrom(gds)); - } - - @Override - public void edgeInserted(V source, V target) { - if (!source.equals(target)) { - deriveTc(source, target, true); - } - } - - @Override - public void edgeDeleted(V source, V target) { - if (!source.equals(target)) { - deriveTc(source, target, false); - } - } - - @Override - public void nodeInserted(V n) { - - } - - @Override - public void nodeDeleted(V n) { - this.tc.deleteTupleEnd(n); - } - - /** - * Derives the transitive closure relation when an edge is inserted or deleted. - * - * @param source - * the source of the edge - * @param target - * the target of the edge - * @param dCount - * the value is -1 if an edge was deleted and +1 if an edge was inserted - */ - private void deriveTc(V source, V target, boolean isInsertion) { - - // if (dCount == 1 && isReachable(target, source)) { - // System.out.println("The graph contains cycle with (" + source + ","+ target + ") edge!"); - // } - - CountingTcRelation dtc = new CountingTcRelation(false); - Set tupEnds = null; - - // 1. d(tc(x,y)) :- d(l(x,y)) - if (tc.updateTuple(source, target, isInsertion)) { - dtc.updateTuple(source, target, true /* deltas implicitly have the same sign as isInsertion*/); - notifyTcObservers(source, target, isInsertion); - } - - // 2. d(tc(x,y)) :- d(l(x,z)) & tc(z,y) - tupEnds = tc.getTupleEnds(target); - if (tupEnds != null) { - for (V tupEnd : tupEnds) { - if (!tupEnd.equals(source)) { - if (tc.updateTuple(source, tupEnd, isInsertion)) { - dtc.updateTuple(source, tupEnd, true /* deltas implicitly have the same sign as isInsertion*/); - notifyTcObservers(source, tupEnd, isInsertion); - } - } - } - } - - // 3. d(tc(x,y)) :- lv(x,z) & d(tc(z,y)) - CountingTcRelation newTuples = dtc; - CountingTcRelation tmp = null; - dtc = new CountingTcRelation(false); - - IMemoryView nodes = null; - - while (!newTuples.isEmpty()) { - - tmp = dtc; - dtc = newTuples; - newTuples = tmp; - newTuples.clear(); - - for (V tS : dtc.getTupleStarts()) { - nodes = gds.getSourceNodes(tS); - for (V nS : nodes.distinctValues()) { - int count = nodes.getCount(nS); - for (int i = 0; i < count; i++) { - tupEnds = dtc.getTupleEnds(tS); - if (tupEnds != null) { - for (V tT : tupEnds) { - if (!nS.equals(tT)) { - if (tc.updateTuple(nS, tT, isInsertion)) { - newTuples.updateTuple(nS, tT, true /* deltas implicitly have the same sign as isInsertion*/); - notifyTcObservers(nS, tT, isInsertion); - } - } - } - } - } - } - } - } - - // System.out.println(tc); - } - - public ITcRelation getTcRelation() { - return this.tc; - } - - public void setTcRelation(CountingTcRelation tc) { - this.tc = tc; - } - - @Override - public boolean isReachable(V source, V target) { - return tc.containsTuple(source, target); - } - - @Override - public void attachObserver(ITcObserver to) { - this.observers.add(to); - - } - - @Override - public void detachObserver(ITcObserver to) { - this.observers.remove(to); - } - - @Override - public Set getAllReachableTargets(V source) { - return tc.getTupleEnds(source); - } - - @Override - public Set getAllReachableSources(V target) { - return tc.getTupleStarts(target); - } - - private void notifyTcObservers(V source, V target, boolean isInsertion) { - if (isInsertion) { - for (ITcObserver o : observers) { - o.tupleInserted(source, target); - } - } else { - for (ITcObserver o : observers) { - o.tupleDeleted(source, target); - } - } - } - - @Override - public void dispose() { - tc.clear(); - this.gds.detachObserver(this); - } - - @Override - public IGraphPathFinder getPathFinder() { - return new DFSPathFinder(gds, this); - } -} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java deleted file mode 100644 index 44abbc3d..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/counting/CountingTcRelation.java +++ /dev/null @@ -1,259 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.counting; - -import java.util.Collections; -import java.util.List; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.topsort.TopologicalSorting; -import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup.ChangeGranularity; - -/** - * Transitive closure relation implementation for the Counting algorithm. - * - * @author Tamas Szabo - * - * @param - */ -public class CountingTcRelation implements ITcRelation { - - private IMultiLookup tuplesForward = null; - private IMultiLookup tuplesBackward = null; - - protected CountingTcRelation(boolean backwardIndexing) { - tuplesForward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); - if (backwardIndexing) - tuplesBackward = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); - } - - protected boolean isEmpty() { - return 0 == this.tuplesForward.countKeys(); - } - - protected void clear() { - this.tuplesForward.clear(); - - if (tuplesBackward != null) { - this.tuplesBackward.clear(); - } - } - - protected void union(CountingTcRelation rA) { - IMultiLookup rForward = rA.tuplesForward; - for (V source : rForward.distinctKeys()) { - IMemoryView targetBag = rForward.lookup(source); - for (V target : targetBag.distinctValues()) { - this.addTuple(source, target, targetBag.getCount(target)); - } - } - } - - public int getCount(V source, V target) { - IMemoryView bucket = tuplesForward.lookup(source); - return bucket == null ? 0 : bucket.getCount(target); - } - - /** - * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false - * otherwise (in this case count is incremented with the given count parameter). - * - * @param source - * the source of the tuple - * @param target - * the target of the tuple - * @param count - * the count of the tuple, must be positive - * @return true if the relation did not contain previously the tuple - */ - public boolean addTuple(V source, V target, int count) { - if (tuplesBackward != null) { - tuplesBackward.addPairPositiveMultiplicity(target, source, count); - } - - ChangeGranularity change = - tuplesForward.addPairPositiveMultiplicity(source, target, count); - - return change != ChangeGranularity.DUPLICATE; - } - - /** - * Derivation count of the tuple (source,target) is incremented or decremented. - * Returns true iff updated to / from zero derivation count. - * @since 1.7 - */ - public boolean updateTuple(V source, V target, boolean isInsertion) { - if (isInsertion) { - if (tuplesBackward != null) { - tuplesBackward.addPair(target, source); - } - ChangeGranularity change = - tuplesForward.addPair(source, target); - return change != ChangeGranularity.DUPLICATE; - } else { - if (tuplesBackward != null) { - tuplesBackward.removePair(target, source); - } - ChangeGranularity change = - tuplesForward.removePair(source, target); - return change != ChangeGranularity.DUPLICATE; - } - } - - public void deleteTupleEnd(V deleted) { - Set sourcesToDelete = CollectionsFactory.createSet(); - Set targetsToDelete = CollectionsFactory.createSet(); - - for (V target : tuplesForward.lookupOrEmpty(deleted).distinctValues()) { - targetsToDelete.add(target); - } - if (tuplesBackward != null) { - for (V source : tuplesBackward.lookupOrEmpty(deleted).distinctValues()) { - sourcesToDelete.add(source); - } - } else { - for (V sourceCandidate : tuplesForward.distinctKeys()) { - if (tuplesForward.lookupOrEmpty(sourceCandidate).containsNonZero(deleted)) - sourcesToDelete.add(sourceCandidate); - } - } - - for (V source : sourcesToDelete) { - int count = tuplesForward.lookupOrEmpty(source).getCount(deleted); - for (int i=0; i< count; ++i) tuplesForward.removePair(source, deleted); - } - for (V target : targetsToDelete) { - int count = tuplesForward.lookupOrEmpty(deleted).getCount(target); - for (int i=0; i< count; ++i) tuplesForward.removePair(deleted, target); - } - - if (tuplesBackward != null) { - for (V source : sourcesToDelete) { - int count = tuplesBackward.lookupOrEmpty(deleted).getCount(source); - for (int i=0; i< count; ++i) tuplesBackward.removePair(deleted, source); - } - for (V target : targetsToDelete) { - int count = tuplesBackward.lookupOrEmpty(target).getCount(deleted); - for (int i=0; i< count; ++i) tuplesBackward.removePair(target, deleted); - } - } - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("TcRelation = "); - - for (V source : tuplesForward.distinctKeys()) { - IMemoryView targets = tuplesForward.lookup(source); - for (V target : targets.distinctValues()) { - sb.append("{(" + source + "," + target + ")," + targets.getCount(target) + "} "); - } - } - - return sb.toString(); - } - - @Override - public Set getTupleEnds(V source) { - IMemoryView tupEnds = tuplesForward.lookup(source); - if (tupEnds == null) - return null; - return tupEnds.distinctValues(); - } - - /** - * Returns the set of nodes from which the target node is reachable, if already computed. - * - * @param target - * the target node - * @return the set of source nodes - * @throws UnsupportedOperationException if backwards index not computed - */ - public Set getTupleStarts(V target) { - if (tuplesBackward != null) { - IMemoryView tupStarts = tuplesBackward.lookup(target); - if (tupStarts == null) - return null; - return tupStarts.distinctValues(); - } else { - throw new UnsupportedOperationException("built without backward indexing"); - } - } - - @Override - public Set getTupleStarts() { - Set nodes = CollectionsFactory.createSet(); - for (V s : tuplesForward.distinctKeys()) { - nodes.add(s); - } - return nodes; - } - - /** - * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise. - * - * @param source - * the source node - * @param target - * the target node - * @return true if tuple is present, false otherwise - */ - public boolean containsTuple(V source, V target) { - return tuplesForward.lookupOrEmpty(source).containsNonZero(target); - } - - @SuppressWarnings("unchecked") - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } else if (obj == null || this.getClass() != obj.getClass()) { - return false; - } else { - CountingTcRelation aTR = (CountingTcRelation) obj; - - return tuplesForward.equals(aTR.tuplesForward); - } - } - - @Override - public int hashCode() { - return tuplesForward.hashCode(); - } - - public static CountingTcRelation createFrom(IBiDirectionalGraphDataSource gds) { - List topologicalSorting = TopologicalSorting.compute(gds); - CountingTcRelation tc = new CountingTcRelation(true); - Collections.reverse(topologicalSorting); - for (V n : topologicalSorting) { - IMemoryView sourceNodes = gds.getSourceNodes(n); - Set tupEnds = tc.getTupleEnds(n); - for (V s : sourceNodes.distinctValues()) { - int count = sourceNodes.getCount(s); - for (int i = 0; i < count; i++) { - tc.updateTuple(s, n, true); - if (tupEnds != null) { - for (V t : tupEnds) { - tc.updateTuple(s, t, true); - } - } - } - } - } - - return tc; - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java deleted file mode 100644 index b92d08bf..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedAlg.java +++ /dev/null @@ -1,308 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.dred; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple; -import tools.refinery.viatra.runtime.base.itc.alg.misc.dfs.DFSAlg; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -/** - * This class is the optimized implementation of the DRED algorithm. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the nodes in the graph data source - */ -public class DRedAlg implements IGraphObserver, ITcDataSource { - - private IGraphDataSource graphDataSource = null; - private DRedTcRelation tc = null; - private DRedTcRelation dtc = null; - private List> observers; - - /** - * Constructs a new DRED algorithm and initializes the transitive closure relation with the given graph data source. - * Attach itself on the graph data source as an observer. - * - * @param gds - * the graph data source instance - */ - public DRedAlg(IGraphDataSource gds) { - this.observers = new ArrayList>(); - this.graphDataSource = gds; - this.tc = new DRedTcRelation(); - this.dtc = new DRedTcRelation(); - initTc(); - graphDataSource.attachObserver(this); - } - - /** - * Constructs a new DRED algorithm and initializes the transitive closure relation with the given relation. Attach - * itself on the graph data source as an observer. - * - * @param gds - * the graph data source instance - * @param tc - * the transitive closure instance - */ - public DRedAlg(IGraphDataSource gds, DRedTcRelation tc) { - this.graphDataSource = gds; - this.tc = tc; - this.dtc = new DRedTcRelation(); - graphDataSource.attachObserver(this); - } - - /** - * Initializes the transitive closure relation. - */ - private void initTc() { - DFSAlg dfsa = new DFSAlg(this.graphDataSource); - this.setTcRelation(dfsa.getTcRelation()); - this.graphDataSource.detachObserver(dfsa); - } - - @Override - public void edgeInserted(V source, V target) { - if (!source.equals(target)) { - Set tupStarts = null; - Set tupEnds = null; - Set> tuples = new HashSet>(); - - if (!source.equals(target) && tc.addTuple(source, target)) { - tuples.add(new Tuple(source, target)); - } - - // 2. d+(tc(x,y)) :- d+(tc(x,z)) & lv(z,y) Descartes product - tupStarts = tc.getTupleStarts(source); - tupEnds = tc.getTupleEnds(target); - - for (V s : tupStarts) { - for (V t : tupEnds) { - if (!s.equals(t) && tc.addTuple(s, t)) { - tuples.add(new Tuple(s, t)); - } - } - } - - // (s, source) -> (source, target) - // tupStarts = tc.getTupleStarts(source); - for (V s : tupStarts) { - if (!s.equals(target) && tc.addTuple(s, target)) { - tuples.add(new Tuple(s, target)); - } - } - - // (source, target) -> (target, t) - // tupEnds = tc.getTupleEnds(target); - for (V t : tupEnds) { - if (!source.equals(t) && tc.addTuple(source, t)) { - tuples.add(new Tuple(source, t)); - } - } - - notifyTcObservers(tuples, 1); - } - } - - @Override - public void edgeDeleted(V source, V target) { - if (!source.equals(target)) { - - // Computing overestimate, Descartes product of A and B sets, where - // A: those nodes from which source is reachable - // B: those nodes which is reachable from target - - Map, Integer> tuples = new HashMap, Integer>(); - Set sources = tc.getTupleStarts(source); - Set targets = tc.getTupleEnds(target); - - tc.removeTuple(source, target); - tuples.put(new Tuple(source, target), -1); - - for (V s : sources) { - for (V t : targets) { - if (!s.equals(t)) { - tc.removeTuple(s, t); - tuples.put(new Tuple(s, t), -1); - } - } - } - - for (V s : sources) { - if (!s.equals(target)) { - tc.removeTuple(s, target); - tuples.put(new Tuple(s, target), -1); - } - } - - for (V t : targets) { - if (!source.equals(t)) { - tc.removeTuple(source, t); - tuples.put(new Tuple(source, t), -1); - } - } - - // System.out.println("overestimate: "+dtc); - - // Modify overestimate with those tuples that have alternative derivations - // 1. q+(tc(x,y)) :- lv(x,y) - for (V s : graphDataSource.getAllNodes()) { - IMemoryView targetNodes = graphDataSource.getTargetNodes(s); - for (Entry entry : targetNodes.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V t = entry.getKey(); - if (!s.equals(t)) { - tc.addTuple(s, t); - Tuple tuple = new Tuple(s, t); - Integer count = tuples.get(tuple); - if (count != null && count == -1) { - tuples.remove(tuple); - } - } - - } - } - } - - // 2. q+(tc(x,y)) :- tcv(x,z) & lv(z,y) - DRedTcRelation newTups = new DRedTcRelation(); - dtc.clear(); - dtc.union(tc); - - while (!dtc.isEmpty()) { - - newTups.clear(); - newTups.union(dtc); - dtc.clear(); - - for (V s : newTups.getTupleStarts()) { - for (V t : newTups.getTupleEnds(s)) { - IMemoryView targetNodes = graphDataSource.getTargetNodes(t); - if (targetNodes != null) { - for (Entry entry : targetNodes.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V tn = entry.getKey(); - if (!s.equals(tn) && tc.addTuple(s, tn)) { - dtc.addTuple(s, tn); - tuples.remove(new Tuple(s, tn)); - } - } - } - } - } - } - } - - notifyTcObservers(tuples.keySet(), -1); - } - } - - @Override - public void nodeInserted(V n) { - // Node inserted does not result new tc tuple. - } - - @Override - public void nodeDeleted(V n) { - // FIXME node deletion may involve the deletion of incoming and outgoing edges too - Set set = tc.getTupleEnds(n); - Set modSet = null; - - // n -> target - modSet = new HashSet(set); - - for (V tn : modSet) { - this.tc.removeTuple(n, tn); - } - - // source -> n - set = tc.getTupleStarts(n); - - modSet = new HashSet(set); - - for (V sn : modSet) { - this.tc.removeTuple(sn, n); - } - } - - public DRedTcRelation getTcRelation() { - return this.tc; - } - - public void setTcRelation(DRedTcRelation tc) { - this.tc = tc; - } - - @Override - public boolean isReachable(V source, V target) { - return tc.containsTuple(source, target); - } - - @Override - public void attachObserver(ITcObserver to) { - this.observers.add(to); - } - - @Override - public void detachObserver(ITcObserver to) { - this.observers.remove(to); - } - - @Override - public Set getAllReachableTargets(V source) { - return tc.getTupleEnds(source); - } - - @Override - public Set getAllReachableSources(V target) { - return tc.getTupleStarts(target); - } - - protected void notifyTcObservers(Set> tuples, int dir) { - for (ITcObserver o : observers) { - for (Tuple t : tuples) { - if (!t.getSource().equals(t.getTarget())) { - if (dir == 1) { - o.tupleInserted(t.getSource(), t.getTarget()); - } - if (dir == -1) { - o.tupleDeleted(t.getSource(), t.getTarget()); - } - } - } - } - } - - @Override - public void dispose() { - tc = null; - dtc = null; - } - - @Override - public IGraphPathFinder getPathFinder() { - return new DFSPathFinder(graphDataSource, this); - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java deleted file mode 100644 index 8543b79c..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/dred/DRedTcRelation.java +++ /dev/null @@ -1,223 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.dred; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.ITcRelation; - -public class DRedTcRelation implements ITcRelation { - - // tc(a,b) means that b is transitively reachable from a - private Map> tuplesForward; - - // data structure to efficiently get those nodes from which a given node is reachable - // symmetric to tuplesForward - private Map> tuplesBackward; - - public DRedTcRelation() { - this.tuplesForward = new HashMap>(); - this.tuplesBackward = new HashMap>(); - } - - public void clear() { - this.tuplesForward.clear(); - this.tuplesBackward.clear(); - } - - public boolean isEmpty() { - return tuplesForward.isEmpty(); - } - - public void removeTuple(V source, V target) { - - // removing tuple from 'forward' tc relation - Set sSet = tuplesForward.get(source); - if (sSet != null) { - sSet.remove(target); - if (sSet.size() == 0) - tuplesForward.remove(source); - } - - // removing tuple from 'backward' tc relation - Set tSet = tuplesBackward.get(target); - if (tSet != null) { - tSet.remove(source); - if (tSet.size() == 0) - tuplesBackward.remove(target); - } - } - - /** - * Returns true if the tc relation did not contain previously such a tuple that is defined by (source,target), false - * otherwise. - * - * @param source - * the source of the tuple - * @param target - * the target of the tuple - * @return true if the relation did not contain previously the tuple - */ - public boolean addTuple(V source, V target) { - - // symmetric modification, it is sufficient to check the return value in one collection - // adding tuple to 'forward' tc relation - Set sSet = tuplesForward.get(source); - if (sSet == null) { - Set newSet = new HashSet(); - newSet.add(target); - tuplesForward.put(source, newSet); - } else { - sSet.add(target); - } - - // adding tuple to 'backward' tc relation - Set tSet = tuplesBackward.get(target); - if (tSet == null) { - Set newSet = new HashSet(); - newSet.add(source); - tuplesBackward.put(target, newSet); - return true; - } else { - boolean ret = tSet.add(source); - return ret; - } - - } - - /** - * Union operation of two tc realtions. - * - * @param rA - * the other tc relation - */ - public void union(DRedTcRelation rA) { - for (V source : rA.tuplesForward.keySet()) { - for (V target : rA.tuplesForward.get(source)) { - this.addTuple(source, target); - } - } - } - - /** - * Computes the difference of this tc relation and the given rA parameter. - * - * @param rA - * the subtrahend relation - */ - public void difference(DRedTcRelation rA) { - for (V source : rA.tuplesForward.keySet()) { - for (V target : rA.tuplesForward.get(source)) { - this.removeTuple(source, target); - } - } - } - - @Override - public Set getTupleEnds(V source) { - Set t = tuplesForward.get(source); - return (t == null) ? new HashSet() : new HashSet(t); - } - - /** - * Returns the set of nodes from which the target node is reachable. - * - * @param target - * the target node - * @return the set of source nodes - */ - public Set getTupleStarts(V target) { - Set t = tuplesBackward.get(target); - return (t == null) ? new HashSet() : new HashSet(t); - } - - @Override - public Set getTupleStarts() { - Set t = tuplesForward.keySet(); - return new HashSet(t); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("TcRelation = "); - - for (Entry> entry : this.tuplesForward.entrySet()) { - V source = entry.getKey(); - for (V target : entry.getValue()) { - sb.append("(" + source + "," + target + ") "); - } - } - return sb.toString(); - } - - /** - * Returns true if a (source, target) node is present in the transitive closure relation, false otherwise. - * - * @param source - * the source node - * @param target - * the target node - * @return true if tuple is present, false otherwise - */ - public boolean containsTuple(V source, V target) { - if (tuplesForward.containsKey(source)) { - if (tuplesForward.get(source).contains(target)) - return true; - } - return false; - } - - @SuppressWarnings("unchecked") - @Override - public boolean equals(Object obj) { - if (this == obj) { - return true; - } - if ((obj == null) || (obj.getClass() != this.getClass())) { - return false; - } - - DRedTcRelation aTR = (DRedTcRelation) obj; - - for (Entry> entry : aTR.tuplesForward.entrySet()) { - V source = entry.getKey(); - for (V target : entry.getValue()) { - if (!this.containsTuple(source, target)) - return false; - } - } - - for (Entry> entry : this.tuplesForward.entrySet()) { - V source = entry.getKey(); - for (V target : entry.getValue()) { - if (!aTR.containsTuple(source, target)) - return false; - } - } - - return true; - } - - @Override - public int hashCode() { - int hash = 7; - hash = 31 * hash + tuplesForward.hashCode(); - hash = 31 * hash + tuplesBackward.hashCode(); - return hash; - } - - public Map> getTuplesForward() { - return tuplesForward; - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java deleted file mode 100644 index 8b8908b1..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/fw/FloydWarshallAlg.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.fw; - -import java.util.HashMap; -import java.util.Map; - -import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -public class FloydWarshallAlg implements IGraphObserver { - - private DRedTcRelation tc = null; - private IBiDirectionalGraphDataSource gds = null; - - public FloydWarshallAlg(IGraphDataSource gds) { - if (gds instanceof IBiDirectionalGraphDataSource) { - this.gds = (IBiDirectionalGraphDataSource) gds; - } else { - this.gds = new IBiDirectionalWrapper(gds); - } - - this.tc = new DRedTcRelation(); - gds.attachObserver(this); - generateTc(); - } - - private void generateTc() { - - tc.clear(); - - int n = gds.getAllNodes().size(); - Map mapForw = new HashMap(); - Map mapBackw = new HashMap(); - int[][] P = new int[n][n]; - - int i, j, k; - - // initialize adjacent matrix - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - P[i][j] = 0; - } - } - - i = 0; - for (V node : gds.getAllNodes()) { - mapForw.put(node, i); - mapBackw.put(i, node); - i++; - } - - for (V source : gds.getAllNodes()) { - IMemoryView targets = gds.getTargetNodes(source); - for (V target : targets.distinctValues()) { - P[mapForw.get(source)][mapForw.get(target)] = 1; - } - } - - for (k = 0; k < n; k++) { - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - P[i][j] = P[i][j] | (P[i][k] & P[k][j]); - } - } - } - - for (i = 0; i < n; i++) { - for (j = 0; j < n; j++) { - if (P[i][j] == 1 && i != j) - tc.addTuple(mapBackw.get(i), mapBackw.get(j)); - } - } - } - - @Override - public void edgeInserted(V source, V target) { - generateTc(); - } - - @Override - public void edgeDeleted(V source, V target) { - generateTc(); - } - - @Override - public void nodeInserted(V n) { - generateTc(); - } - - @Override - public void nodeDeleted(V n) { - generateTc(); - } - - public DRedTcRelation getTcRelation() { - return this.tc; - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java deleted file mode 100644 index fdf64f77..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/CountingListener.java +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.alg.incscc; - -import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; -import tools.refinery.viatra.runtime.matchers.util.Direction; - -/** - * @author Tamas Szabo - * - */ -public class CountingListener implements ITcObserver { - - private IncSCCAlg alg; - - public CountingListener(IncSCCAlg alg) { - this.alg = alg; - } - - @Override - public void tupleInserted(V source, V target) { - alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.INSERT); - } - - @Override - public void tupleDeleted(V source, V target) { - alg.notifyTcObservers(alg.sccs.getPartition(source), alg.sccs.getPartition(target), Direction.DELETE); - } - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java deleted file mode 100644 index 5d720e75..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/incscc/IncSCCAlg.java +++ /dev/null @@ -1,645 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.incscc; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Objects; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.counting.CountingAlg; -import tools.refinery.viatra.runtime.base.itc.alg.misc.bfs.BFS; -import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; -import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCCResult; -import tools.refinery.viatra.runtime.base.itc.alg.util.CollectionHelper; -import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; -import tools.refinery.viatra.runtime.base.itc.alg.misc.DFSPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.GraphHelper; -import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; -import tools.refinery.viatra.runtime.base.itc.alg.misc.Tuple; -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcObserver; -import tools.refinery.viatra.runtime.matchers.algorithms.UnionFind; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.Direction; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -/** - * Incremental SCC maintenance + counting algorithm. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the nodes in the graph data source - */ -public class IncSCCAlg implements IGraphObserver, ITcDataSource { - - public UnionFind sccs; - public IBiDirectionalGraphDataSource gds; - private CountingAlg counting; - private Graph reducedGraph; - private IBiDirectionalGraphDataSource reducedGraphIndexer; - private List> observers; - private CountingListener countingListener; - - public IncSCCAlg(IGraphDataSource graphDataSource) { - - if (graphDataSource instanceof IBiDirectionalGraphDataSource) { - gds = (IBiDirectionalGraphDataSource) graphDataSource; - } else { - gds = new IBiDirectionalWrapper(graphDataSource); - } - observers = CollectionsFactory.createObserverList(); - sccs = new UnionFind(); - reducedGraph = new Graph(); - reducedGraphIndexer = new IBiDirectionalWrapper(reducedGraph); - countingListener = new CountingListener(this); - initalizeInternalDataStructures(); - gds.attachObserver(this); - } - - private void initalizeInternalDataStructures() { - SCCResult _sccres = SCC.computeSCC(gds); - Set> _sccs = _sccres.getSccs(); - - for (Set _set : _sccs) { - sccs.makeSet(_set); - } - - // Initalization of the reduced graph - for (V n : sccs.getPartitionHeads()) { - reducedGraph.insertNode(n); - } - - for (V source : gds.getAllNodes()) { - final IMemoryView targetNodes = gds.getTargetNodes(source); - for (Entry entry : targetNodes.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V target = entry.getKey(); - V sourceRoot = sccs.find(source); - V targetRoot = sccs.find(target); - - if (!sourceRoot.equals(targetRoot)) { - reducedGraph.insertEdge(sourceRoot, targetRoot); - } - } - } - } - - counting = new CountingAlg(reducedGraph); - } - - @Override - public void edgeInserted(V source, V target) { - V sourceRoot = sccs.find(source); - V targetRoot = sccs.find(target); - - // Different SCC - if (!sourceRoot.equals(targetRoot)) { - - // source is reachable from target? - if (counting.isReachable(targetRoot, sourceRoot)) { - - Set predecessorRoots = counting.getAllReachableSources(sourceRoot); - Set successorRoots = counting.getAllReachableTargets(targetRoot); - - // 1. intersection of source and target roots, these will be in the merged SCC - Set isectRoots = CollectionHelper.intersection(predecessorRoots, successorRoots); - isectRoots.add(sourceRoot); - isectRoots.add(targetRoot); - - // notifications must be issued before Union-Find modifications - if (observers.size() > 0) { - Set sourceSCCs = createSetNullTolerant(predecessorRoots); - sourceSCCs.add(sourceRoot); - Set targetSCCs = createSetNullTolerant(successorRoots); - targetSCCs.add(targetRoot); - - // tracing back to actual nodes - for (V sourceSCC : sourceSCCs) { - targetLoop: for (V targetSCC : targetSCCs) { - if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; - - boolean needsNotification = - // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. - // Issue notifications only if there is no self-loop present at the moment - (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper - .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) - || - // Case 2. sourceSCC and targetSCC are different sccs. - (!sourceSCC.equals(targetSCC)); - // if self loop is already present omit the notification - if (needsNotification) { - notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), - Direction.INSERT); - } - } - } - } - - // 2. delete edges, nodes - List sourceSCCs = new ArrayList(); - List targetSCCs = new ArrayList(); - - for (V r : isectRoots) { - List sourceSCCsOfSCC = getSourceSCCsOfSCC(r); - List targetSCCsOfSCC = getTargetSCCsOfSCC(r); - - for (V sourceSCC : sourceSCCsOfSCC) { - if (!sourceSCC.equals(r)) { - reducedGraph.deleteEdgeIfExists(sourceSCC, r); - } - } - - for (V targetSCC : targetSCCsOfSCC) { - if (!isectRoots.contains(targetSCC) && !r.equals(targetSCC)) { - reducedGraph.deleteEdgeIfExists(r, targetSCC); - } - } - - sourceSCCs.addAll(sourceSCCsOfSCC); - targetSCCs.addAll(targetSCCsOfSCC); - } - - for (V r : isectRoots) { - reducedGraph.deleteNode(r); - } - - // 3. union - Iterator iterator = isectRoots.iterator(); - V newRoot = iterator.next(); - while (iterator.hasNext()) { - newRoot = sccs.union(newRoot, iterator.next()); - } - - // 4. add new node - reducedGraph.insertNode(newRoot); - - // 5. add edges - Set containedNodes = sccs.getPartition(newRoot); - - for (V sourceSCC : sourceSCCs) { - if (!containedNodes.contains(sourceSCC) && !sourceSCC.equals(newRoot)) { - reducedGraph.insertEdge(sourceSCC, newRoot); - } - } - for (V targetSCC : targetSCCs) { - if (!containedNodes.contains(targetSCC) && !targetSCC.equals(newRoot)) { - reducedGraph.insertEdge(newRoot, targetSCC); - } - } - } else { - if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 1) { - counting.attachObserver(countingListener); - } - reducedGraph.insertEdge(sourceRoot, targetRoot); - counting.detachObserver(countingListener); - } - } else { - // Notifications about self-loops - if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 - && GraphHelper.getEdgeCount(source, target, gds) == 1) { - notifyTcObservers(source, source, Direction.INSERT); - } - } - } - - @Override - public void edgeDeleted(V source, V target) { - V sourceRoot = sccs.find(source); - V targetRoot = sccs.find(target); - - if (!sourceRoot.equals(targetRoot)) { - if (observers.size() > 0 && GraphHelper.getEdgeCount(source, target, gds) == 0) { - counting.attachObserver(countingListener); - } - reducedGraph.deleteEdgeIfExists(sourceRoot, targetRoot); - counting.detachObserver(countingListener); - } else { - // get the graph for the scc whose root is sourceRoot - Graph g = GraphHelper.getSubGraph(sccs.getPartition(sourceRoot), gds); - - // if source is not reachable from target anymore - if (!BFS.isReachable(source, target, g)) { - // create copies of the current state before destructive manipulation - Map reachableSources = CollectionsFactory.createMap(); - for (Entry entry : reducedGraphIndexer.getSourceNodes(sourceRoot).entriesWithMultiplicities()) { - reachableSources.put(entry.getKey(), entry.getValue()); - } - Map reachableTargets = CollectionsFactory.createMap(); - for (Entry entry : reducedGraphIndexer.getTargetNodes(sourceRoot).entriesWithMultiplicities()) { - reachableTargets.put(entry.getKey(), entry.getValue()); - } - - SCCResult _newSccs = SCC.computeSCC(g); - - // delete scc node (and with its edges too) - for (Entry entry : reachableSources.entrySet()) { - V s = entry.getKey(); - for (int i = 0; i < entry.getValue(); i++) { - reducedGraph.deleteEdgeIfExists(s, sourceRoot); - } - } - - for (Entry entry : reachableTargets.entrySet()) { - V t = entry.getKey(); - for (int i = 0; i < entry.getValue(); i++) { - reducedGraph.deleteEdgeIfExists(sourceRoot, t); - } - } - - sccs.deleteSet(sourceRoot); - reducedGraph.deleteNode(sourceRoot); - - Set> newSCCs = _newSccs.getSccs(); - Set newSCCRoots = CollectionsFactory.createSet(); - - // add new nodes and edges to the reduced graph - for (Set newSCC : newSCCs) { - V newRoot = sccs.makeSet(newSCC); - reducedGraph.insertNode(newRoot); - newSCCRoots.add(newRoot); - } - for (V newSCCRoot : newSCCRoots) { - List sourceSCCsOfSCC = getSourceSCCsOfSCC(newSCCRoot); - List targetSCCsOfSCC = getTargetSCCsOfSCC(newSCCRoot); - - for (V sourceSCC : sourceSCCsOfSCC) { - if (!sourceSCC.equals(newSCCRoot)) { - reducedGraph.insertEdge(sccs.find(sourceSCC), newSCCRoot); - } - } - for (V targetSCC : targetSCCsOfSCC) { - if (!newSCCRoots.contains(targetSCC) && !targetSCC.equals(newSCCRoot)) - reducedGraph.insertEdge(newSCCRoot, targetSCC); - } - } - - // Must be after the union-find modifications - if (observers.size() > 0) { - V newSourceRoot = sccs.find(source); - V newTargetRoot = sccs.find(target); - - Set sourceSCCs = createSetNullTolerant(counting.getAllReachableSources(newSourceRoot)); - sourceSCCs.add(newSourceRoot); - - Set targetSCCs = createSetNullTolerant(counting.getAllReachableTargets(newTargetRoot)); - targetSCCs.add(newTargetRoot); - - for (V sourceSCC : sourceSCCs) { - targetLoop: for (V targetSCC : targetSCCs) { - if (counting.isReachable(sourceSCC, targetSCC)) continue targetLoop; - - boolean needsNotification = - // Case 1. sourceSCC and targetSCC are the same and it is a one sized scc. - // Issue notifications only if there is no self-loop present at the moment - (sourceSCC.equals(targetSCC) && sccs.getPartition(sourceSCC).size() == 1 && GraphHelper - .getEdgeCount(sccs.getPartition(sourceSCC).iterator().next(), gds) == 0) - || - // Case 2. sourceSCC and targetSCC are different sccs. - (!sourceSCC.equals(targetSCC)); - // if self loop is already present omit the notification - if (needsNotification) { - notifyTcObservers(sccs.getPartition(sourceSCC), sccs.getPartition(targetSCC), - Direction.DELETE); - } - } - } - } - } else { - // only handle self-loop notifications - sourceRoot equals to targetRoot - if (observers.size() > 0 && sccs.getPartition(sourceRoot).size() == 1 - && GraphHelper.getEdgeCount(source, target, gds) == 0) { - notifyTcObservers(source, source, Direction.DELETE); - } - } - } - } - - @Override - public void nodeInserted(V n) { - sccs.makeSet(n); - reducedGraph.insertNode(n); - } - - @Override - public void nodeDeleted(V n) { - IMemoryView sources = gds.getSourceNodes(n); - IMemoryView targets = gds.getTargetNodes(n); - - for (Entry entry : sources.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V source = entry.getKey(); - edgeDeleted(source, n); - } - } - - for (Entry entry : targets.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V target = entry.getKey(); - edgeDeleted(n, target); - } - } - - sccs.deleteSet(n); - } - - @Override - public void attachObserver(ITcObserver to) { - observers.add(to); - } - - @Override - public void detachObserver(ITcObserver to) { - observers.remove(to); - } - - @Override - public Set getAllReachableTargets(V source) { - V sourceRoot = sccs.find(source); - Set containedNodes = sccs.getPartition(sourceRoot); - Set targets = CollectionsFactory.createSet(); - - if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(source, gds) == 1) { - targets.addAll(containedNodes); - } - - Set rootSet = counting.getAllReachableTargets(sourceRoot); - if (rootSet != null) { - for (V _root : rootSet) { - targets.addAll(sccs.getPartition(_root)); - } - } - - return targets; - } - - @Override - public Set getAllReachableSources(V target) { - V targetRoot = sccs.find(target); - Set containedNodes = sccs.getPartition(targetRoot); - Set sources = CollectionsFactory.createSet(); - - if (containedNodes.size() > 1 || GraphHelper.getEdgeCount(target, gds) == 1) { - sources.addAll(containedNodes); - } - - Set rootSet = counting.getAllReachableSources(targetRoot); - if (rootSet != null) { - for (V _root : rootSet) { - sources.addAll(sccs.getPartition(_root)); - } - } - return sources; - } - - @Override - public boolean isReachable(V source, V target) { - V sourceRoot = sccs.find(source); - V targetRoot = sccs.find(target); - - if (sourceRoot.equals(targetRoot)) - return true; - else - return counting.isReachable(sourceRoot, targetRoot); - } - - public List getReachabilityPath(V source, V target) { - if (!isReachable(source, target)) { - return null; - } else { - Set sccsInSubGraph = CollectionHelper.intersection(counting.getAllReachableTargets(source), - counting.getAllReachableSources(target)); - sccsInSubGraph.add(sccs.find(source)); - sccsInSubGraph.add(sccs.find(target)); - Set nodesInSubGraph = CollectionsFactory.createSet(); - - for (V sccRoot : sccsInSubGraph) { - nodesInSubGraph.addAll(sccs.getPartition(sccRoot)); - } - - return GraphHelper.constructPath(source, target, nodesInSubGraph, gds); - } - } - - // for JUnit - public boolean checkTcRelation(DRedTcRelation tc) { - - for (V s : tc.getTupleStarts()) { - for (V t : tc.getTupleEnds(s)) { - if (!isReachable(s, t)) - return false; - } - } - - for (V root : counting.getTcRelation().getTupleStarts()) { - for (V end : counting.getTcRelation().getTupleEnds(root)) { - for (V s : sccs.getPartition(root)) { - for (V t : sccs.getPartition(end)) { - if (!tc.containsTuple(s, t)) - return false; - } - } - } - } - - return true; - } - - /** - * Return the SCCs from which the SCC represented by the root node is reachable. Note that an SCC can be present - * multiple times in the returned list (multiple edges between the two SCCs). - * - * @param root - * @return the list of reachable target SCCs - */ - private List getSourceSCCsOfSCC(V root) { - List sourceSCCs = new ArrayList(); - - for (V containedNode : this.sccs.getPartition(root)) { - IMemoryView sourceNodes = this.gds.getSourceNodes(containedNode); - for (V source : sourceNodes.distinctValues()) { - sourceSCCs.add(this.sccs.find(source)); - } - } - - return sourceSCCs; - } - - /** - * Returns true if the SCC represented by the given root node has incoming edges in the reduced graph, - * false otherwise (if this SCC is a source in the reduced graph). - * - * @param root the root node of an SCC - * @return true if it has incoming edges, false otherwise - * @since 1.6 - */ - public boolean hasIncomingEdges(final V root) { - for (final V containedNode : this.sccs.getPartition(root)) { - final IMemoryView sourceNodes = this.gds.getSourceNodes(containedNode); - for (final V source : sourceNodes.distinctValues()) { - final V otherRoot = this.sccs.find(source); - if (!Objects.equals(root, otherRoot)) { - return true; - } - } - } - return false; - } - - /** - * Return the SCCs which are reachable from the SCC represented by the root node. Note that an SCC can be present - * multiple times in the returned list (multiple edges between the two SCCs). - * - * @param root - * @return the list of reachable target SCCs - */ - private List getTargetSCCsOfSCC(V root) { - List targetSCCs = new ArrayList(); - - for (V containedNode : this.sccs.getPartition(root)) { - IMemoryView targetNodes = this.gds.getTargetNodes(containedNode); - for (V target : targetNodes.distinctValues()) { - targetSCCs.add(this.sccs.find(target)); - } - } - - return targetSCCs; - } - - /** - * Returns true if the SCC represented by the given root node has outgoing edges in the reduced graph, - * false otherwise (if this SCC is a sink in the reduced graph). - * - * @param root the root node of an SCC - * @return true if it has outgoing edges, false otherwise - * @since 1.6 - */ - public boolean hasOutgoingEdges(V root) { - for (final V containedNode : this.sccs.getPartition(root)) { - final IMemoryView targetNodes = this.gds.getTargetNodes(containedNode); - for (final V target : targetNodes.distinctValues()) { - final V otherRoot = this.sccs.find(target); - if (!Objects.equals(root, otherRoot)) { - return true; - } - } - } - return false; - } - - @Override - public void dispose() { - gds.detachObserver(this); - counting.dispose(); - } - - /** - * Call this method to notify the observers of the transitive closure relation. The tuples used in the notification - * will be the Descartes product of the two sets given. - * - * @param sources - * the source nodes - * @param targets - * the target nodes - * @param direction - */ - protected void notifyTcObservers(Set sources, Set targets, Direction direction) { - for (V s : sources) { - for (V t : targets) { - notifyTcObservers(s, t, direction); - } - } - } - - private void notifyTcObservers(V source, V target, Direction direction) { - for (ITcObserver observer : observers) { - if (direction == Direction.INSERT) { - observer.tupleInserted(source, target); - } - if (direction == Direction.DELETE) { - observer.tupleDeleted(source, target); - } - } - } - - /** - * Returns the node that is selected as the representative of the SCC containing the argument. - * @since 1.6 - */ - public V getRepresentative(V node) { - return sccs.find(node); - } - - public Set> getTcRelation() { - Set> resultSet = new HashSet>(); - - for (V sourceRoot : sccs.getPartitionHeads()) { - Set sources = sccs.getPartition(sourceRoot); - if (sources.size() > 1 || GraphHelper.getEdgeCount(sources.iterator().next(), gds) == 1) { - for (V source : sources) { - for (V target : sources) { - resultSet.add(new Tuple(source, target)); - } - } - } - - Set reachableTargets = counting.getAllReachableTargets(sourceRoot); - if (reachableTargets != null) { - for (V targetRoot : reachableTargets) { - for (V source : sources) { - for (V target : sccs.getPartition(targetRoot)) { - resultSet.add(new Tuple(source, target)); - } - } - } - } - } - - return resultSet; - } - - public boolean isIsolated(V node) { - IMemoryView targets = gds.getTargetNodes(node); - IMemoryView sources = gds.getSourceNodes(node); - return targets.isEmpty() && sources.isEmpty(); - } - - @Override - public IGraphPathFinder getPathFinder() { - return new DFSPathFinder(gds, this); - } - - /** - * The graph of SCCs; each SCC is represented by its representative node (see {@link #getRepresentative(Object)}) - * @since 1.6 - */ - public Graph getReducedGraph() { - return reducedGraph; - } - - private static Set createSetNullTolerant(Set initial) { - if (initial != null) - return CollectionsFactory.createSet(initial); - else - return CollectionsFactory.createSet(); - } - - -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java deleted file mode 100644 index 51017b1a..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/DFSPathFinder.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2016, Abel Hegedus and IncQueryLabs Ltd. - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -import java.util.ArrayList; -import java.util.Deque; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -/** - * A depth-first search implementation of the {@link IGraphPathFinder}. - * - * TODO use ITC to filter nodes that must be traversed, instead of checks - * - * @author Abel Hegedus - * - * @param - * the node type of the graph - */ -public class DFSPathFinder implements IGraphPathFinder { - - private IGraphDataSource graph; - private ITcDataSource itc; - - public DFSPathFinder(IGraphDataSource graph, ITcDataSource itc) { - this.graph = graph; - this.itc = itc; - } - - @Override - public Iterable> getAllPaths(V sourceNode, V targetNode) { - Set endNodes = new HashSet(); - endNodes.add(targetNode); - return getAllPathsToTargets(sourceNode, endNodes); - } - - @Override - public Iterable> getAllPathsToTargets(V sourceNode, Set targetNodes) { - List> paths = new ArrayList>(); - Deque visited = new LinkedList(); - Set reachableTargets = new HashSet(); - for (V targetNode : targetNodes) { - if (itc.isReachable(sourceNode, targetNode)) { - reachableTargets.add(targetNode); - } - } - if (!reachableTargets.isEmpty()) { - return paths; - } - visited.add(sourceNode); - return getPaths(paths, visited, reachableTargets); - } - - protected Iterable> getPaths(List> paths, Deque visited, Set targetNodes) { - IMemoryView nodes = graph.getTargetNodes(visited.getLast()); - // examine adjacent nodes - for (V node : nodes.distinctValues()) { - if (visited.contains(node)) { - continue; - } - if (targetNodes.contains(node)) { - visited.add(node); - // clone visited LinkedList - Deque visitedClone = new LinkedList(visited); - paths.add(visitedClone); - visited.removeLast(); - break; - } - } - - // in breadth-first, recursion needs to come after visiting connected nodes - for (V node : nodes.distinctValues()) { - if (visited.contains(node) || targetNodes.contains(node)) { - continue; - } - boolean canReachTarget = false; - for (V target : targetNodes) { - if (itc.isReachable(node, target)) { - canReachTarget = true; - break; - } - } - if (canReachTarget) { - visited.addLast(node); - getPaths(paths, visited, targetNodes); - visited.removeLast(); - } - } - - return paths; - } - - public String printPaths(List> paths) { - StringBuilder sb = new StringBuilder(); - for (Deque visited : paths) { - sb.append("Path: "); - for (V node : visited) { - sb.append(node); - sb.append(" --> "); - } - sb.append("\n"); - } - return sb.toString(); - } - - @Override - public Deque getPath(V sourceNode, V targetNode) { - // TODO optimize - Iterable> allPaths = getAllPaths(sourceNode, targetNode); - Iterator> pathIterator = allPaths.iterator(); - return pathIterator.hasNext() ? pathIterator.next() : new LinkedList(); - } - - @Override - public Iterable> getShortestPaths(V sourceNode, V targetNode) { - // TODO optimize - Iterable> allPaths = getAllPaths(sourceNode, targetNode); - List> shortestPaths = new ArrayList>(); - int shortestPathLength = -1; - for (Deque path : allPaths) { - int pathLength = path.size(); - if (shortestPathLength == -1 || pathLength < shortestPathLength) { - shortestPaths.clear(); - shortestPathLength = pathLength; - } - if (pathLength == shortestPathLength) { - shortestPaths.add(path); - } - } - return shortestPaths; - } - -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java deleted file mode 100644 index cf68d36a..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Edge.java +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -public class Edge { - private V source; - private V target; - - public Edge(V source, V target) { - super(); - this.source = source; - this.target = target; - } - - public V getSource() { - return source; - } - - public void setSource(V source) { - this.source = source; - } - - public V getTarget() { - return target; - } - - public void setTarget(V target) { - this.target = target; - } - -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java deleted file mode 100644 index 194e979b..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/GraphHelper.java +++ /dev/null @@ -1,173 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Map.Entry; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -/** - * Utility class for graph related operations. - * - * @author Tamas Szabo - */ -public class GraphHelper { - - private GraphHelper() {/*Utility class constructor*/} - - /** - * Returns the subgraph from the given {@link IBiDirectionalGraphDataSource} which contains the given set of nodes. - * - * @param nodesInSubGraph - * the nodes that are present in the subgraph - * @param graphDataSource - * the graph data source for the original graph - * @return the subgraph associated to the given nodes - */ - public static Graph getSubGraph(Collection nodesInSubGraph, - IBiDirectionalGraphDataSource graphDataSource) { - Graph g = new Graph(); - if (nodesInSubGraph != null) { - for (V node : nodesInSubGraph) { - g.insertNode(node); - } - - for (V node : nodesInSubGraph) { - IMemoryView sources = graphDataSource.getSourceNodes(node); - for (Entry entry : sources.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V s = entry.getKey(); - if (nodesInSubGraph.contains(s)) { - g.insertEdge(s, node); - } - } - } - } - } - - return g; - } - - /** - * Constructs a path between source and target in the given graph. Both the {@link IGraphDataSource} and the set of - * nodes are used, this way it is possible to construct a path in a given subgraph. - * - * The returned {@link List} contains the nodes along the path (this means that there is an edge in the graph - * between two consecutive nodes). A self loop (one edge) is indicated with the source node being present two times - * in the returned {@link List}. - * - * @param source - * the source node - * @param target - * the target node - * @param nodesInGraph - * the nodes that are present in the subgraph - * @param graphDataSource - * the graph data source - * @return the path between the two nodes - */ - public static List constructPath(V source, V target, Set nodesInGraph, - IGraphDataSource graphDataSource) { - Set visitedNodes = new HashSet(); - List path = new ArrayList(); - - visitedNodes.add(source); - path.add(source); - V act = source; - - // if source and target are the same node - if (source.equals(target) && graphDataSource.getTargetNodes(source).containsNonZero(target)) { - // the node will be present in the path two times - path.add(source); - return path; - } else { - while (act != null) { - V nextNode = getNextNodeToVisit(act, graphDataSource, nodesInGraph, visitedNodes); - if (nextNode == null && path.size() > 1) { - // needs to backtrack along path - // remove the last element in the path because we can't go - // anywhere from there - path.remove(path.size() - 1); - while (nextNode == null && path.size() > 0) { - V lastPathElement = path.get(path.size() - 1); - nextNode = getNextNodeToVisit(lastPathElement, graphDataSource, nodesInGraph, visitedNodes); - if (nextNode == null) { - path.remove(path.size() - 1); - } - } - } - - if (nextNode != null) { - visitedNodes.add(nextNode); - path.add(nextNode); - if (nextNode.equals(target)) { - return path; - } - } - act = nextNode; - } - return null; - } - } - - private static V getNextNodeToVisit(V act, IGraphDataSource graphDataSource, Set nodesInSubGraph, - Set visitedNodes) { - IMemoryView targetNodes = graphDataSource.getTargetNodes(act); - for (Entry entry : targetNodes.entriesWithMultiplicities()) { - for (int i = 0; i < entry.getValue(); i++) { - V node = entry.getKey(); - if (nodesInSubGraph.contains(node) && !visitedNodes.contains(node)) { - return node; - } - } - } - return null; - } - - /** - * Returns the number of self-loop edges for the given node. - * - * @param node - * the node - * @param graphDataSource - * the graph data source - * @return the number of self-loop edges - */ - public static int getEdgeCount(V node, IGraphDataSource graphDataSource) { - return getEdgeCount(node, node, graphDataSource); - } - - /** - * Returns the number of edges between the given source and target nodes. - * - * @param source - * the source node - * @param target - * the target node - * @param graphDataSource - * the graph data source - * @return the number of parallel edges between the two nodes - */ - public static int getEdgeCount(V source, V target, IGraphDataSource graphDataSource) { - Integer count = graphDataSource.getTargetNodes(source).getCount(target); - if (count == null) { - return 0; - } else { - return count; - } - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java deleted file mode 100644 index cebb09c8..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/IGraphPathFinder.java +++ /dev/null @@ -1,67 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2013, Abel Hegedus, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -import java.util.Deque; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.igraph.ITcDataSource; - -/** - * The path finder provides methods for retrieving paths in a graph between a source node and one or more target nodes. - * Use {@link ITcDataSource#getPathFinder()} for instantiating. - * - * @author Abel Hegedus - * - * @param the node type of the graph - */ -public interface IGraphPathFinder { - - /** - * Returns an arbitrary path from the source node to the target node (if such exists). If there is no path - * between them, an empty collection is returned. - * - * @param sourceNode the source node of the path - * @param targetNode the target node of the path - * @return the path from the source to the target, or empty collection if target is not reachable from source. - */ - Deque getPath(V sourceNode, V targetNode); - - /** - * Returns the collection of shortest paths from the source node to the target node (if such exists). If there is no path - * between them, an empty collection is returned. - * - * @param sourceNode the source node of the path - * @param targetNode the target node of the path - * @return the collection of shortest paths from the source to the target, or empty collection if target is not reachable from source. - */ - Iterable> getShortestPaths(V sourceNode, V targetNode); - - /** - * Returns the collection of paths from the source node to the target node (if such exists). If there is no path - * between them, an empty collection is returned. - * - * @param sourceNode the source node of the path - * @param targetNode the target node of the path - * @return the collection of paths from the source to the target, or empty collection if target is not reachable from source. - */ - Iterable> getAllPaths(V sourceNode, V targetNode); - - /** - * Returns the collection of paths from the source node to any of the target nodes (if such exists). If there is no path - * between them, an empty collection is returned. - * - * @param sourceNode the source node of the path - * @param targetNodes the set of target nodes of the paths - * @return the collection of paths from the source to any of the targets, or empty collection if neither target is reachable from source. - */ - Iterable> getAllPathsToTargets(V sourceNode, Set targetNodes); - - -} \ No newline at end of file diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java deleted file mode 100644 index a41ff6c7..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/ITcRelation.java +++ /dev/null @@ -1,31 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -import java.util.Set; - -public interface ITcRelation { - - /** - * Returns the starting nodes from a transitive closure relation. - * - * @return the set of starting nodes - */ - public Set getTupleStarts(); - - /** - * Returns the set of nodes that are reachable from the given node. - * - * @param start - * the starting node - * @return the set of reachable nodes - */ - public Set getTupleEnds(V start); -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java deleted file mode 100644 index a2d54a59..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/Tuple.java +++ /dev/null @@ -1,60 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc; - -public class Tuple { - - private V source; - private V target; - - public Tuple(V source, V target) { - super(); - this.source = source; - this.target = target; - } - - public V getSource() { - return source; - } - - public void setSource(V source) { - this.source = source; - } - - public V getTarget() { - return target; - } - - public void setTarget(V target) { - this.target = target; - } - - @Override - public String toString() { - return "(" + source.toString() + "," + target.toString() + ")"; - } - - @Override - public boolean equals(Object o) { - if (o instanceof Tuple) { - Tuple t = (Tuple) o; - - if (t.getSource().equals(this.source) && t.getTarget().equals(this.target)) { - return true; - } - } - return false; - } - - @Override - public int hashCode() { - return source.hashCode() + target.hashCode(); - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java deleted file mode 100644 index 798f31d2..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/bfs/BFS.java +++ /dev/null @@ -1,151 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.bfs; - -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; - -public class BFS { - - private BFS() {/*Utility class constructor*/} - - /** - * Performs a breadth first search on the given graph to determine whether source is reachable from target. - * - * @param - * the type parameter of the nodes in the graph - * @param source - * the source node - * @param target - * the target node - * @param graph - * the graph data source - * @return true if source is reachable from target, false otherwise - */ - public static boolean isReachable(V source, V target, IGraphDataSource graph) { - List nodeQueue = new ArrayList(); - Set visited = new HashSet(); - - nodeQueue.add(source); - visited.add(source); - - boolean ret = _isReachable(target, graph, nodeQueue, visited); - return ret; - } - - private static boolean _isReachable(V target, IGraphDataSource graph, List nodeQueue, Set visited) { - - while (!nodeQueue.isEmpty()) { - V node = nodeQueue.remove(0); - for (V t : graph.getTargetNodes(node).distinctValues()){ - if (t.equals(target)) { - return true; - } - if (!visited.contains(t)) { - visited.add(t); - nodeQueue.add(t); - } - } - } - return false; - } - - public static Set reachableSources(IBiDirectionalGraphDataSource graph, V target) { - Set retSet = new HashSet(); - retSet.add(target); - List nodeQueue = new ArrayList(); - nodeQueue.add(target); - - _reachableSources(graph, nodeQueue, retSet); - - return retSet; - } - - private static void _reachableSources(IBiDirectionalGraphDataSource graph, List nodeQueue, - Set retSet) { - while (!nodeQueue.isEmpty()) { - V node = nodeQueue.remove(0); - for (V _node : graph.getSourceNodes(node).distinctValues()) { - if (!retSet.contains(_node)) { - retSet.add(_node); - nodeQueue.add(_node); - } - } - } - } - - public static Set reachableTargets(IGraphDataSource graph, V source) { - Set retSet = new HashSet(); - retSet.add(source); - List nodeQueue = new ArrayList(); - nodeQueue.add(source); - - _reachableTargets(graph, nodeQueue, retSet); - - return retSet; - } - - private static void _reachableTargets(IGraphDataSource graph, List nodeQueue, Set retSet) { - while (!nodeQueue.isEmpty()) { - V node = nodeQueue.remove(0); - - for (V _node : graph.getTargetNodes(node).distinctValues()) { - - if (!retSet.contains(_node)) { - retSet.add(_node); - nodeQueue.add(_node); - } - } - } - } - - /** - * Performs a breadth first search on the given graph and collects all the nodes along the path from source to - * target if such path exists. - * - * @param - * the type parameter of the nodes in the graph - * @param source - * the source node - * @param target - * the target node - * @param graph - * the graph data source - * @return the set of nodes along the path - */ - public static Set collectNodesAlongPath(V source, V target, IGraphDataSource graph) { - Set path = new HashSet(); - _collectNodesAlongPath(source, target, graph, path); - return path; - } - - private static boolean _collectNodesAlongPath(V node, V target, IGraphDataSource graph, Set path) { - - boolean res = false; - - // end recursion - if (node.equals(target)) { - path.add(node); - return true; - } else { - for (V _nodeT : graph.getTargetNodes(node).distinctValues()) { - res = (_collectNodesAlongPath(_nodeT, target, graph, path)) || res; - } - if (res) - path.add(node); - return res; - } - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java deleted file mode 100644 index c8d25c4e..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/dfs/DFSAlg.java +++ /dev/null @@ -1,93 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.dfs; - -import java.util.HashMap; - -import tools.refinery.viatra.runtime.base.itc.alg.dred.DRedTcRelation; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; - -public class DFSAlg implements IGraphObserver { - - private IGraphDataSource gds; - private DRedTcRelation tc; - private int[] visited; - private HashMap nodeMap; - - public DFSAlg(IGraphDataSource gds) { - this.gds = gds; - this.tc = new DRedTcRelation(); - gds.attachObserver(this); - deriveTc(); - } - - private void deriveTc() { - tc.clear(); - - this.visited = new int[gds.getAllNodes().size()]; - nodeMap = new HashMap(); - - int j = 0; - for (V n : gds.getAllNodes()) { - nodeMap.put(n, j); - j++; - } - - for (V n : gds.getAllNodes()) { - oneDFS(n, n); - initVisitedArray(); - } - } - - private void initVisitedArray() { - for (int i = 0; i < visited.length; i++) - visited[i] = 0; - } - - private void oneDFS(V act, V source) { - - if (!act.equals(source)) { - tc.addTuple(source, act); - } - - visited[nodeMap.get(act)] = 1; - - for (V t : gds.getTargetNodes(act).distinctValues()) { - if (visited[nodeMap.get(t)] == 0) { - oneDFS(t, source); - } - } - } - - public DRedTcRelation getTcRelation() { - return this.tc; - } - - @Override - public void edgeInserted(V source, V target) { - deriveTc(); - } - - @Override - public void edgeDeleted(V source, V target) { - deriveTc(); - } - - @Override - public void nodeInserted(V n) { - deriveTc(); - } - - @Override - public void nodeDeleted(V n) { - deriveTc(); - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java deleted file mode 100644 index c99a48ab..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/PKAlg.java +++ /dev/null @@ -1,179 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalWrapper; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; - -public class PKAlg implements IGraphObserver { - - /** - * Maps the nodes to their indicies. - */ - private Map node2index; - private Map index2node; - private Map node2mark; - - /** - * Maps the index of a node to the index in the topsort. - */ - private Map index2topsort; - private Map topsort2index; - - /** - * Index associated to the inserted nodes (incrementing with every insertion). - */ - private int index; - - /** - * Index within the topsort for the target node when edge insertion occurs. - */ - private int lower_bound; - - /** - * Index within the topsort for the source node when edge insertion occurs. - */ - private int upper_bound; - - private List RF; - private List RB; - private IBiDirectionalGraphDataSource gds; - - public PKAlg(IGraphDataSource gds) { - if (gds instanceof IBiDirectionalGraphDataSource) { - this.gds = (IBiDirectionalGraphDataSource) gds; - } else { - this.gds = new IBiDirectionalWrapper(gds); - } - - node2mark = new HashMap(); - node2index = new HashMap(); - index2node = new HashMap(); - index2topsort = new HashMap(); - topsort2index = new HashMap(); - index = 0; - - gds.attachObserver(this); - } - - @Override - public void edgeInserted(V source, V target) { - - RF = new ArrayList(); - RB = new ArrayList(); - - lower_bound = index2topsort.get(node2index.get(target)); - upper_bound = index2topsort.get(node2index.get(source)); - - if (lower_bound < upper_bound) { - dfsForward(target); - dfsBackward(source); - reorder(); - } - } - - private List getIndicies(List list) { - List indicies = new ArrayList(); - - for (V n : list) - indicies.add(index2topsort.get(node2index.get(n))); - - return indicies; - } - - private void reorder() { - - Collections.reverse(RB); - - // azon csomopontok indexei amelyek sorrendje nem jo - List L = getIndicies(RF); - L.addAll(getIndicies(RB)); - Collections.sort(L); - - for (int i = 0; i < RB.size(); i++) { - index2topsort.put(node2index.get(RB.get(i)), L.get(i)); - topsort2index.put(L.get(i), node2index.get(RB.get(i))); - } - - for (int i = 0; i < RF.size(); i++) { - index2topsort.put(node2index.get(RF.get(i)), L.get(i + RB.size())); - topsort2index.put(L.get(i + RB.size()), node2index.get(RF.get(i))); - } - } - - @SuppressWarnings("unused") - private List getTopSort() { - List topsort = new ArrayList(); - - for (int i : topsort2index.values()) { - topsort.add(index2node.get(i)); - } - - return topsort; - } - - private void dfsBackward(V node) { - node2mark.put(node, true); - RB.add(node); - - for (V sn : gds.getSourceNodes(node).distinctValues()) { - int top_id = index2topsort.get(node2index.get(sn)); - - if (!node2mark.get(sn) && lower_bound < top_id) - dfsBackward(sn); - } - } - - private void dfsForward(V node) { - node2mark.put(node, true); - RF.add(node); - - for (V tn : gds.getTargetNodes(node).distinctValues()) { - int top_id = index2topsort.get(node2index.get(tn)); - - if (top_id == upper_bound) - System.out.println("!!!Cycle detected!!!"); - else if (!node2mark.get(tn) && top_id < upper_bound) - dfsForward(tn); - } - } - - @Override - public void edgeDeleted(V source, V target) { - // Edge deletion does not affect topsort - } - - @Override - public void nodeInserted(V n) { - node2mark.put(n, false); - node2index.put(n, index); - index2node.put(index, n); - index2topsort.put(index, index); - topsort2index.put(index, index); - index++; - } - - @Override - public void nodeDeleted(V n) { - node2mark.remove(n); - int node_id = node2index.remove(n); - index2node.remove(node_id); - int top_id = index2topsort.remove(node_id); - topsort2index.remove(top_id); - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java deleted file mode 100644 index 8915998b..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCC.java +++ /dev/null @@ -1,146 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; - -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; - -/** - * Efficient algorithms to compute the Strongly Connected Components in a directed graph. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the nodes in the graph - */ -public class SCC { - - private SCC() {/*Utility class constructor*/} - - public static long sccId = 0; - - /** - * Computes the SCCs for the given graph and returns them as a multiset. (Iterative version of Tarjan's algorithm) - * - * @param g - * the directed graph data source - * @return the set of SCCs - */ - public static SCCResult computeSCC(IGraphDataSource g) { - int index = 0; - Set> ret = new HashSet>(); - - // stores the lowlink and index information for the given node - Map nodeMap = CollectionsFactory.createMap(); - - // stores all target nodes of a given node - the list will be modified - Map> targetNodeMap = CollectionsFactory.createMap(); - - // stores those target nodes for a given node which have not been visited - Map> notVisitedMap = CollectionsFactory.createMap(); - - // stores the nodes during the traversal - Stack nodeStack = new Stack(); - - // stores the nodes which belong to an scc (there can be many sccs in the stack at the same time) - Stack sccStack = new Stack(); - - boolean sink = false, finishedTraversal = true; - - // initialize all nodes with 0 index and 0 lowlink - Set allNodes = g.getAllNodes(); - for (V n : allNodes) { - nodeMap.put(n, new SCCProperty(0, 0)); - } - - for (V n : allNodes) { - // if the node has not been visited yet - if (nodeMap.get(n).getIndex() == 0) { - nodeStack.push(n); - - while (!nodeStack.isEmpty()) { - V currentNode = nodeStack.peek(); - sink = false; - finishedTraversal = false; - SCCProperty prop = nodeMap.get(currentNode); - - if (nodeMap.get(currentNode).getIndex() == 0) { - index++; - sccStack.push(currentNode); - prop.setIndex(index); - prop.setLowlink(index); - - notVisitedMap.put(currentNode, new HashSet()); - - // storing the target nodes of the actual node - if (g.getTargetNodes(currentNode) != null) { - Set targets = g.getTargetNodes(currentNode).distinctValues(); - targetNodeMap.put(currentNode, CollectionsFactory.createSet(targets)); - } - } - - if (targetNodeMap.get(currentNode) != null) { - - // remove node from stack, the exploration of its children has finished - if (targetNodeMap.get(currentNode).size() == 0) { - targetNodeMap.remove(currentNode); - - nodeStack.pop(); - - for (V targetNode : g.getTargetNodes(currentNode).distinctValues()) { - if (notVisitedMap.get(currentNode).contains(targetNode)) { - prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getLowlink())); - } else if (sccStack.contains(targetNode)) { - prop.setLowlink(Math.min(prop.getLowlink(), nodeMap.get(targetNode).getIndex())); - } - } - - finishedTraversal = true; - } else { - V targetNode = targetNodeMap.get(currentNode).iterator().next(); - targetNodeMap.get(currentNode).remove(targetNode); - // if the targetNode has not yet been visited push it to the stack - // and mark it in the notVisitedMap - if (nodeMap.get(targetNode).getIndex() == 0) { - notVisitedMap.get(currentNode).add(targetNode); - nodeStack.add(targetNode); - } - } - } - // if currentNode has no target nodes - else { - nodeStack.pop(); - sink = true; - } - - // create scc if node is a sink or an scc has been found - if ((sink || finishedTraversal) && (prop.getLowlink() == prop.getIndex())) { - Set sc = new HashSet(); - V targetNode = null; - - do { - targetNode = sccStack.pop(); - sc.add(targetNode); - } while (!targetNode.equals(currentNode)); - - ret.add(sc); - } - } - } - } - - return new SCCResult(ret, g); - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java deleted file mode 100644 index b26e3d37..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCProperty.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; - -public class SCCProperty { - private int index; - private int lowlink; - - public SCCProperty(int index, int lowlink) { - super(); - this.index = index; - this.lowlink = lowlink; - } - - public int getIndex() { - return index; - } - - public void setIndex(int index) { - this.index = index; - } - - public int getLowlink() { - return lowlink; - } - - public void setLowlink(int lowlink) { - this.lowlink = lowlink; - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java deleted file mode 100644 index fde59d3b..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/scc/SCCResult.java +++ /dev/null @@ -1,81 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.scc; - -import java.util.Map.Entry; -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; - -public class SCCResult { - - private Set> sccs; - private IGraphDataSource gds; - - public SCCResult(Set> sccs, IGraphDataSource gds) { - this.sccs = sccs; - this.gds = gds; - } - - public Set> getSccs() { - return sccs; - } - - public int getSCCCount() { - return sccs.size(); - } - - public double getAverageNodeCount() { - double a = 0; - - for (Set s : sccs) { - a += s.size(); - } - - return a / sccs.size(); - } - - public double getAverageEdgeCount() { - long edgeSum = 0; - - for (Set scc : sccs) { - for (V source : scc) { - for (Entry entry : gds.getTargetNodes(source).entriesWithMultiplicities()) { - if (scc.contains(entry.getKey())) { - edgeSum += entry.getValue(); - } - } - } - } - - return (double) edgeSum / (double) sccs.size(); - } - - public int getBiggestSCCSize() { - int max = 0; - - for (Set scc : sccs) { - if (scc.size() > max) - max = scc.size(); - } - - return max; - } - - public long getSumOfSquares() { - long sum = 0; - - for (Set scc : sccs) { - sum += scc.size() * scc.size(); - } - - return sum; - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java deleted file mode 100644 index dd18e6c8..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/misc/topsort/TopologicalSorting.java +++ /dev/null @@ -1,77 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.alg.misc.topsort; - -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.Stack; - -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; - -/** - * @since 1.6 - */ -public class TopologicalSorting { - - private TopologicalSorting() {/*Utility class constructor*/} - - private static final class Pair { - public T element; - public boolean isParent; - - public Pair(final T element, final boolean isParent) { - this.element = element; - this.isParent = isParent; - } - } - - /** - * Returns a topological ordering for the given graph data source. - * Output format: if there is an a -> b (transitive) reachability, then node a will come before node b in the resulting list. - * - * @param gds the graph data source - * @return a topological ordering - */ - public static List compute(final IGraphDataSource gds) { - final Set visited = new HashSet(); - final LinkedList result = new LinkedList(); - final Stack> dfsStack = new Stack>(); - - for (final T node : gds.getAllNodes()) { - if (!visited.contains(node)) { - dfsStack.push(new Pair(node, false)); - } - - while (!dfsStack.isEmpty()) { - final Pair head = dfsStack.pop(); - final T source = head.element; - - if (head.isParent) { - // we have already seen source, push it to the resulting stack - result.addFirst(source); - } else { - // first time we see source, continue with its children - visited.add(source); - dfsStack.push(new Pair(source, true)); - - for (final T target : gds.getTargetNodes(source).distinctValues()) { - if (!visited.contains(target)) { - dfsStack.push(new Pair(target, false)); - } - } - } - } - } - - return result; - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java deleted file mode 100644 index 51015404..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeElectionAlgorithm.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.viatra.runtime.base.itc.alg.representative; - -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.matchers.util.Direction; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -public abstract class RepresentativeElectionAlgorithm implements IGraphObserver { - protected final Graph graph; - protected final Map representatives = new HashMap<>(); - protected final Map> components = new HashMap<>(); - private RepresentativeObserver observer; - - protected RepresentativeElectionAlgorithm(Graph graph) { - this.graph = graph; - initializeComponents(); - graph.attachObserver(this); - } - - protected abstract void initializeComponents(); - - protected void initializeSet(Set set) { - var iterator = set.iterator(); - if (!iterator.hasNext()) { - // Set is empty. - return; - } - var representative = iterator.next(); - for (var node : set) { - var oldRepresentative = representatives.put(node, representative); - if (oldRepresentative != null && !representative.equals(oldRepresentative)) { - throw new IllegalStateException("Node %s is already in a set represented by %s, cannot add it to %s" - .formatted(node, oldRepresentative, set)); - } - } - components.put(representative, set); - } - - protected void merge(Object leftRepresentative, Object rightRepresentative) { - if (leftRepresentative.equals(rightRepresentative)) { - return; - } - var leftSet = getComponent(leftRepresentative); - var rightSet = getComponent(rightRepresentative); - if (leftSet.size() < rightSet.size()) { - merge(rightRepresentative, rightSet, leftRepresentative, leftSet); - } else { - merge(leftRepresentative, leftSet, rightRepresentative, rightSet); - } - } - - private void merge(Object preservedRepresentative, Set preservedSet, Object removedRepresentative, - Set removedSet) { - components.remove(removedRepresentative); - for (var node : removedSet) { - representatives.put(node, preservedRepresentative); - preservedSet.add(node); - notifyToObservers(node, removedRepresentative, preservedRepresentative); - } - } - - protected void assignNewRepresentative(Object oldRepresentative, Set set) { - var iterator = set.iterator(); - if (!iterator.hasNext()) { - return; - } - var newRepresentative = iterator.next(); - components.put(newRepresentative, set); - for (var node : set) { - var oldRepresentativeOfNode = representatives.put(node, newRepresentative); - if (!oldRepresentative.equals(oldRepresentativeOfNode)) { - throw new IllegalArgumentException("Node %s was not represented by %s but by %s" - .formatted(node, oldRepresentative, oldRepresentativeOfNode)); - } - notifyToObservers(node, oldRepresentative, newRepresentative); - } - } - - public void setObserver(RepresentativeObserver observer) { - this.observer = observer; - } - - public Map> getComponents() { - return components; - } - - public Object getRepresentative(Object node) { - return representatives.get(node); - } - - public Set getComponent(Object representative) { - return components.get(representative); - } - - public void dispose() { - graph.detachObserver(this); - } - - @Override - public void nodeInserted(Object n) { - var component = new HashSet<>(1); - component.add(n); - initializeSet(component); - notifyToObservers(n, n, Direction.INSERT); - } - - @Override - public void nodeDeleted(Object n) { - var representative = representatives.remove(n); - if (!representative.equals(n)) { - throw new IllegalStateException("Trying to delete node with dangling edges"); - } - components.remove(representative); - notifyToObservers(n, representative, Direction.DELETE); - } - - protected void notifyToObservers(Object node, Object oldRepresentative, Object newRepresentative) { - notifyToObservers(node, oldRepresentative, Direction.DELETE); - notifyToObservers(node, newRepresentative, Direction.INSERT); - } - - protected void notifyToObservers(Object node, Object representative, Direction direction) { - if (observer != null) { - observer.tupleChanged(node, representative, direction); - } - } - - public interface Factory { - RepresentativeElectionAlgorithm create(Graph graph); - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java deleted file mode 100644 index 93cce1ea..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/RepresentativeObserver.java +++ /dev/null @@ -1,12 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.viatra.runtime.base.itc.alg.representative; - -import tools.refinery.viatra.runtime.matchers.util.Direction; - -public interface RepresentativeObserver { - void tupleChanged(Object node, Object representative, Direction direction); -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java deleted file mode 100644 index ba42bb13..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/StronglyConnectedComponentAlgorithm.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.viatra.runtime.base.itc.alg.representative; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.GraphHelper; -import tools.refinery.viatra.runtime.base.itc.alg.misc.bfs.BFS; -import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; - -import java.util.Collection; -import java.util.Set; - -public class StronglyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { - public StronglyConnectedComponentAlgorithm(Graph graph) { - super(graph); - } - - @Override - protected void initializeComponents() { - var computedSCCs = SCC.computeSCC(graph).getSccs(); - for (var computedSCC : computedSCCs) { - initializeSet(computedSCC); - } - } - - @Override - public void edgeInserted(Object source, Object target) { - var sourceRoot = getRepresentative(source); - var targetRoot = getRepresentative(target); - if (sourceRoot.equals(targetRoot)) { - // New edge does not change strongly connected components. - return; - } - if (BFS.isReachable(target, source, graph)) { - merge(sourceRoot, targetRoot); - } - } - - @Override - public void edgeDeleted(Object source, Object target) { - var sourceRoot = getRepresentative(source); - var targetRoot = getRepresentative(target); - if (!sourceRoot.equals(targetRoot)) { - // New edge does not change strongly connected components. - return; - } - var component = GraphHelper.getSubGraph(getComponent(sourceRoot), graph); - if (!BFS.isReachable(source, target, component)) { - var newSCCs = SCC.computeSCC(component).getSccs(); - split(sourceRoot, newSCCs); - } - } - - private void split(Object preservedRepresentative, Collection> sets) { - for (var set : sets) { - if (set.contains(preservedRepresentative)) { - components.put(preservedRepresentative, set); - } else { - assignNewRepresentative(preservedRepresentative, set); - } - } - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java deleted file mode 100644 index 22159499..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/representative/WeaklyConnectedComponentAlgorithm.java +++ /dev/null @@ -1,85 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The Refinery Authors - * - * SPDX-License-Identifier: EPL-2.0 - */ -package tools.refinery.viatra.runtime.base.itc.alg.representative; - -import tools.refinery.viatra.runtime.base.itc.graphimpl.Graph; - -import java.util.ArrayDeque; -import java.util.HashSet; -import java.util.Set; - -public class WeaklyConnectedComponentAlgorithm extends RepresentativeElectionAlgorithm { - public WeaklyConnectedComponentAlgorithm(Graph graph) { - super(graph); - } - - @Override - protected void initializeComponents() { - for (var node : graph.getAllNodes()) { - if (representatives.containsKey(node)) { - continue; - } - var reachable = getReachableNodes(node); - initializeSet(reachable); - } - } - - @Override - public void edgeInserted(Object source, Object target) { - var sourceRoot = getRepresentative(source); - var targetRoot = getRepresentative(target); - merge(sourceRoot, targetRoot); - } - - @Override - public void edgeDeleted(Object source, Object target) { - var sourceRoot = getRepresentative(source); - var targetRoot = getRepresentative(target); - if (!sourceRoot.equals(targetRoot)) { - throw new IllegalArgumentException("Trying to remove edge not in graph"); - } - var targetReachable = getReachableNodes(target); - if (!targetReachable.contains(source)) { - split(sourceRoot, targetReachable); - } - } - - private void split(Object sourceRepresentative, Set targetReachable) { - var sourceComponent = getComponent(sourceRepresentative); - sourceComponent.removeAll(targetReachable); - if (targetReachable.contains(sourceRepresentative)) { - components.put(sourceRepresentative, targetReachable); - assignNewRepresentative(sourceRepresentative, sourceComponent); - } else { - assignNewRepresentative(sourceRepresentative, targetReachable); - } - } - - private Set getReachableNodes(Object source) { - var retSet = new HashSet<>(); - retSet.add(source); - var nodeQueue = new ArrayDeque<>(); - nodeQueue.addLast(source); - - while (!nodeQueue.isEmpty()) { - var node = nodeQueue.removeFirst(); - for (var neighbor : graph.getTargetNodes(node).distinctValues()) { - if (!retSet.contains(neighbor)) { - retSet.add(neighbor); - nodeQueue.addLast(neighbor); - } - } - for (var neighbor : graph.getSourceNodes(node).distinctValues()) { - if (!retSet.contains(neighbor)) { - retSet.add(neighbor); - nodeQueue.addLast(neighbor); - } - } - } - - return retSet; - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java deleted file mode 100644 index c9b3cafa..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/alg/util/CollectionHelper.java +++ /dev/null @@ -1,64 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.alg.util; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; - -/** - * @author Tamas Szabo - * - */ -public class CollectionHelper { - - private CollectionHelper() {/*Utility class constructor*/} - - /** - * Returns the intersection of two sets. It calls {@link Set#retainAll(java.util.Collection)} but returns a new set - * containing the elements of the intersection. - * - * @param set1 - * the first set (can be null, interpreted as empty) - * @param set2 - * the second set (can be null, interpreted as empty) - * @return the intersection of the sets - * @since 1.7 - */ - public static Set intersection(Set set1, Set set2) { - if (set1 == null || set2 == null) - return CollectionsFactory.createSet(); - - Set intersection = CollectionsFactory.createSet(set1); - intersection.retainAll(set2); - return intersection; - } - - - /** - * Returns the difference of two sets (S1\S2). It calls {@link Set#removeAll(java.util.Collection)} but returns a - * new set containing the elements of the difference. - * - * @param set1 - * the first set (can be null, interpreted as empty) - * @param set2 - * the second set (can be null, interpreted as empty) - * @return the difference of the sets - * @since 1.7 - */ - public static Set difference(Set set1, Set set2) { - if (set1 == null) - return CollectionsFactory.createSet(); - - Set difference = CollectionsFactory.createSet(set1); - if (set2 != null) difference.removeAll(set2); - return difference; - } - -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java deleted file mode 100644 index 0e21f323..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/DotGenerator.java +++ /dev/null @@ -1,160 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2019, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ -package tools.refinery.viatra.runtime.base.itc.graphimpl; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCC; -import tools.refinery.viatra.runtime.base.itc.alg.misc.scc.SCCResult; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; - -import java.util.HashMap; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - -/** - * This class contains utility methods to generate dot representations for {@link Graph} instances. - * - * @author Tamas Szabo - * @since 2.3 - */ -public class DotGenerator { - - private static final String[] colors = new String[] { "yellow", "blue", "red", "green", "gray", "cyan" }; - - private DotGenerator() { - - } - - /** - * Generates the dot representation for the given graph. - * - * @param graph - * the graph - * @param colorSCCs - * specifies if the strongly connected components with size greater than shall be colored - * @param nameFunction - * use this function to provide custom names to nodes, null if the default toString shall be used - * @param colorFunction - * use this function to provide custom color to nodes, null if the default white color shall be used - * @param edgeFunction - * use this function to provide custom edge labels, null if no edge label shall be printed - * @return the dot representation as a string - */ - public static String generateDot(final Graph graph, final boolean colorSCCs, - final Function nameFunction, final Function colorFunction, - final Function> edgeFunction) { - final Map colorMap = new HashMap(); - - if (colorSCCs) { - final SCCResult result = SCC.computeSCC(graph); - final Set> sccs = result.getSccs(); - - int i = 0; - for (final Set scc : sccs) { - if (scc.size() > 1) { - for (final V node : scc) { - final String color = colorMap.get(node); - if (color == null) { - colorMap.put(node, colors[i % colors.length]); - } else { - colorMap.put(node, colorMap.get(node) + ":" + colors[i % colors.length]); - } - } - i++; - } - } - - // if a node has no color yet, then make it white - for (final V node : graph.getAllNodes()) { - if (!colorMap.containsKey(node)) { - colorMap.put(node, "white"); - } - } - } else { - for (final V node : graph.getAllNodes()) { - colorMap.put(node, "white"); - } - } - - if (colorFunction != null) { - for (final V node : graph.getAllNodes()) { - colorMap.put(node, colorFunction.apply(node)); - } - } - - final StringBuilder builder = new StringBuilder(); - builder.append("digraph g {\n"); - - for (final V node : graph.getAllNodes()) { - final String nodePresentation = nameFunction == null ? node.toString() : nameFunction.apply(node); - builder.append("\"" + nodePresentation + "\""); - builder.append("[style=filled,fillcolor=" + colorMap.get(node) + "]"); - builder.append(";\n"); - } - - for (final V source : graph.getAllNodes()) { - final IMemoryView targets = graph.getTargetNodes(source); - if (!targets.isEmpty()) { - final String sourcePresentation = nameFunction == null ? source.toString() : nameFunction.apply(source); - for (final V target : targets.distinctValues()) { - String edgeLabel = null; - if (edgeFunction != null) { - final Function v1 = edgeFunction.apply(source); - if (v1 != null) { - edgeLabel = v1.apply(target); - } - } - - final String targetPresentation = nameFunction == null ? target.toString() - : nameFunction.apply(target); - - builder.append("\"" + sourcePresentation + "\" -> \"" + targetPresentation + "\""); - if (edgeLabel != null) { - builder.append("[label=\"" + edgeLabel + "\"]"); - } - builder.append(";\n"); - } - } - } - - builder.append("}"); - return builder.toString(); - } - - /** - * Generates the dot representation for the given graph. No special pretty printing customization will be applied. - * - * @param graph - * the graph - * @return the dot representation as a string - */ - public static String generateDot(final Graph graph) { - return generateDot(graph, false, null, null, null); - } - - /** - * Returns a simple name shortener function that can be used in the graphviz visualization to help with readability. - * WARNING: if you shorten the name of the {@link Node}s too much, the visualization may become incorrect because - * grahpviz will treat different nodes as the same if their shortened names are the same. - * - * @param maxLength - * the maximum length of the text that is kept from the toString of the objects in the graph - * @return the shrunk toString value - */ - public static Function getNameShortener(final int maxLength) { - return new Function() { - @Override - public String apply(final V obj) { - final String value = obj.toString(); - return value.substring(0, Math.min(value.length(), maxLength)); - } - }; - } - -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java deleted file mode 100644 index 4267579c..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/graphimpl/Graph.java +++ /dev/null @@ -1,185 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.graphimpl; - -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphDataSource; -import tools.refinery.viatra.runtime.base.itc.igraph.IGraphObserver; -import tools.refinery.viatra.runtime.base.itc.igraph.IBiDirectionalGraphDataSource; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; - -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; - -public class Graph implements IGraphDataSource, IBiDirectionalGraphDataSource { - - // source -> target -> count - private IMultiLookup outgoingEdges; - // target -> source -> count - private IMultiLookup incomingEdges; - - private Set nodes; - - private List> observers; - - public Graph() { - outgoingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); - incomingEdges = CollectionsFactory.createMultiLookup(Object.class, MemoryType.MULTISETS, Object.class); - nodes = CollectionsFactory.createSet(); - observers = CollectionsFactory.createObserverList(); - } - - public void insertEdge(V source, V target) { - outgoingEdges.addPair(source, target); - incomingEdges.addPair(target, source); - - for (IGraphObserver go : observers) { - go.edgeInserted(source, target); - } - } - - /** - * No-op if trying to delete edge that does not exist - * - * @since 2.0 - * @see #deleteEdgeIfExists(Object, Object) - */ - public void deleteEdgeIfExists(V source, V target) { - boolean containedEdge = outgoingEdges.lookupOrEmpty(source).containsNonZero(target); - if (containedEdge) { - deleteEdgeThatExists(source, target); - } - } - - /** - * @throws IllegalStateException - * if trying to delete edge that does not exist - * @since 2.0 - * @see #deleteEdgeIfExists(Object, Object) - */ - public void deleteEdgeThatExists(V source, V target) { - outgoingEdges.removePair(source, target); - incomingEdges.removePair(target, source); - for (IGraphObserver go : observers) { - go.edgeDeleted(source, target); - } - } - - /** - * @deprecated use explicitly {@link #deleteEdgeThatExists(Object, Object)} or - * {@link #deleteEdgeIfExists(Object, Object)} instead. To preserve backwards compatibility, this method - * delegates to the latter. - * - */ - @Deprecated - public void deleteEdge(V source, V target) { - deleteEdgeIfExists(source, target); - } - - /** - * Insert the given node into the graph. - */ - public void insertNode(V node) { - if (nodes.add(node)) { - for (IGraphObserver go : observers) { - go.nodeInserted(node); - } - } - } - - /** - * Deletes the given node AND all of the edges going in and out from the node. - */ - public void deleteNode(V node) { - if (nodes.remove(node)) { - IMemoryView incomingView = incomingEdges.lookup(node); - if (incomingView != null) { - Map incoming = CollectionsFactory.createMap(incomingView.asMap()); - - for (Entry entry : incoming.entrySet()) { - for (int i = 0; i < entry.getValue(); i++) { - deleteEdgeThatExists(entry.getKey(), node); - } - } - } - - IMemoryView outgoingView = outgoingEdges.lookup(node); - if (outgoingView != null) { - Map outgoing = CollectionsFactory.createMap(outgoingView.asMap()); - - for (Entry entry : outgoing.entrySet()) { - for (int i = 0; i < entry.getValue(); i++) { - deleteEdgeThatExists(node, entry.getKey()); - } - } - } - - for (IGraphObserver go : observers) { - go.nodeDeleted(node); - } - } - } - - @Override - public void attachObserver(IGraphObserver go) { - observers.add(go); - } - - @Override - public void attachAsFirstObserver(IGraphObserver observer) { - observers.add(0, observer); - } - - @Override - public void detachObserver(IGraphObserver go) { - observers.remove(go); - } - - @Override - public Set getAllNodes() { - return nodes; - } - - @Override - public IMemoryView getTargetNodes(V source) { - return outgoingEdges.lookupOrEmpty(source); - } - - @Override - public IMemoryView getSourceNodes(V target) { - return incomingEdges.lookupOrEmpty(target); - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("nodes = "); - for (V n : getAllNodes()) { - sb.append(n.toString()); - sb.append(" "); - } - sb.append(" edges = "); - for (V source : outgoingEdges.distinctKeys()) { - IMemoryView targets = outgoingEdges.lookup(source); - for (V target : targets.distinctValues()) { - int count = targets.getCount(target); - for (int i = 0; i < count; i++) { - sb.append("(" + source + "," + target + ") "); - } - } - } - return sb.toString(); - } - -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java deleted file mode 100644 index 64659447..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalGraphDataSource.java +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiset; - -/** - * A bi-directional graph data source supports all operations that an {@link IGraphDataSource} does, but it - * also makes it possible to query the incoming edges of nodes, not only the outgoing edges. - * - * @author Tamas Szabo - * - * @param the type of the nodes in the graph - */ -public interface IBiDirectionalGraphDataSource extends IGraphDataSource { - - /** - * Returns the source nodes for the given target node. - * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source. - * - * The method must not return null. - * - * @param target the target node - * @return the multiset of source nodes - * @since 2.0 - */ - public IMemoryView getSourceNodes(V target); - -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java deleted file mode 100644 index becab0eb..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IBiDirectionalWrapper.java +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -import java.util.Set; - -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; -import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory.MemoryType; -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiLookup; - -/** - * This class can be used to wrap an {@link IGraphDataSource} into an {@link IBiDirectionalGraphDataSource}. This class - * provides support for the retrieval of source nodes for a given target which is not supported by standard - * {@link IGraphDataSource} implementations. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the nodes in the graph data source - */ -public class IBiDirectionalWrapper implements IBiDirectionalGraphDataSource, IGraphObserver { - - private IGraphDataSource wrappedDataSource; - // target -> source -> count - private IMultiLookup incomingEdges; - - public IBiDirectionalWrapper(IGraphDataSource gds) { - this.wrappedDataSource = gds; - - this.incomingEdges = CollectionsFactory.createMultiLookup( - Object.class, MemoryType.MULTISETS, Object.class); - - if (gds.getAllNodes() != null) { - for (V source : gds.getAllNodes()) { - IMemoryView targets = gds.getTargetNodes(source); - for (V target : targets.distinctValues()) { - int count = targets.getCount(target); - for (int i = 0; i < count; i++) { - edgeInserted(source, target); - } - } - } - } - - gds.attachAsFirstObserver(this); - } - - @Override - public void attachObserver(IGraphObserver observer) { - wrappedDataSource.attachObserver(observer); - } - - @Override - public void attachAsFirstObserver(IGraphObserver observer) { - wrappedDataSource.attachAsFirstObserver(observer); - } - - @Override - public void detachObserver(IGraphObserver observer) { - wrappedDataSource.detachObserver(observer); - } - - @Override - public Set getAllNodes() { - return wrappedDataSource.getAllNodes(); - } - - @Override - public IMemoryView getTargetNodes(V source) { - return wrappedDataSource.getTargetNodes(source); - } - - @Override - public IMemoryView getSourceNodes(V target) { - return incomingEdges.lookupOrEmpty(target); - } - - @Override - public void edgeInserted(V source, V target) { - incomingEdges.addPair(target, source); - } - - @Override - public void edgeDeleted(V source, V target) { - incomingEdges.removePair(target, source); - } - - @Override - public void nodeInserted(V n) { - - } - - @Override - public void nodeDeleted(V node) { - - } - - @Override - public String toString() { - return wrappedDataSource.toString(); - } -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java deleted file mode 100644 index 3fa65936..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphDataSource.java +++ /dev/null @@ -1,70 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -import tools.refinery.viatra.runtime.matchers.util.IMemoryView; -import tools.refinery.viatra.runtime.matchers.util.IMultiset; - -import java.util.Set; - -/** - * The interface prescribes the set of operations that a graph data source must support. - *

Note that the old version of the interface is broken at version 1.6; - * MultiSets are now presented as Maps instead of Lists. - * - * @author Tamas Szabo - * - * @param - * the type of the nodes in the graph - */ -public interface IGraphDataSource { - - /** - * Attaches a new graph observer to this graph data source. Observers will be notified in the order they have been registered. - * - * @param observer the graph observer - */ - public void attachObserver(IGraphObserver observer); - - /** - * Attaches a new graph observer to this graph data source as the first one. - * In the notification order this observer will be the first one as long as another call to this method happens. - * - * @param observer the graph observer - * @since 1.6 - */ - public void attachAsFirstObserver(IGraphObserver observer); - - /** - * Detaches an already registered graph observer from this graph data source. - * - * @param observer the graph observer - */ - public void detachObserver(IGraphObserver observer); - - /** - * Returns the complete set of nodes in the graph data source. - * - * @return the set of all nodes - */ - public Set getAllNodes(); - - /** - * Returns the target nodes for the given source node. - * The returned data structure is an {@link IMultiset} because of potential parallel edges in the graph data source. - * - * The method must not return null. - * - * @param source the source node - * @return the multiset of target nodes - * @since 2.0 - */ - public IMemoryView getTargetNodes(V source); -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java deleted file mode 100644 index 5cb2d9fa..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/IGraphObserver.java +++ /dev/null @@ -1,55 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -/** - * Interface GraphObserver is used to observ the changes in a graph; edge and node insertion/deleteion. - * - * @author Tamas Szabo - * - */ -public interface IGraphObserver { - - /** - * Used to notify when an edge is inserted into the graph. - * - * @param source - * the source of the edge - * @param target - * the target of the edge - */ - public void edgeInserted(V source, V target); - - /** - * Used to notify when an edge is deleted from the graph. - * - * @param source - * the source of the edge - * @param target - * the target of the edge - */ - public void edgeDeleted(V source, V target); - - /** - * Used to notify when a node is inserted into the graph. - * - * @param n - * the node - */ - public void nodeInserted(V n); - - /** - * Used to notify when a node is deleted from the graph. - * - * @param n - * the node - */ - public void nodeDeleted(V n); -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java deleted file mode 100644 index 5924b723..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcDataSource.java +++ /dev/null @@ -1,82 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -import java.util.Set; - -import tools.refinery.viatra.runtime.base.itc.alg.misc.IGraphPathFinder; - -/** - * This interface defines those methods that a transitive reachability data source should provide. - * - * @author Tamas Szabo - * - * @param - * the type parameter of the node - */ -public interface ITcDataSource { - - /** - * Attach a transitive closure relation observer. - * - * @param to - * the observer object - */ - public void attachObserver(ITcObserver to); - - /** - * Detach a transitive closure relation observer. - * - * @param to - * the observer object - */ - public void detachObserver(ITcObserver to); - - /** - * Returns all nodes which are reachable from the source node. - * - * @param source - * the source node - * @return the set of target nodes - */ - public Set getAllReachableTargets(V source); - - /** - * Returns all nodes from which the target node is reachable. - * - * @param target - * the target node - * @return the set of source nodes - */ - public Set getAllReachableSources(V target); - - /** - * Returns true if the target node is reachable from the source node. - * - * @param source - * the source node - * @param target - * the target node - * @return true if target is reachable from source, false otherwise - */ - public boolean isReachable(V source, V target); - - /** - * The returned {@link IGraphPathFinder} can be used to retrieve paths between nodes using transitive reachability. - * - * @return a path finder for the graph. - */ - public IGraphPathFinder getPathFinder(); - - /** - * Call this method to properly dispose the data structures of a transitive closure algorithm. - */ - public void dispose(); -} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java deleted file mode 100644 index fded53f1..00000000 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/base/itc/igraph/ITcObserver.java +++ /dev/null @@ -1,39 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2010-2012, Tamas Szabo, Istvan Rath and Daniel Varro - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License v. 2.0 which is available at - * http://www.eclipse.org/legal/epl-v20.html. - * - * SPDX-License-Identifier: EPL-2.0 - *******************************************************************************/ - -package tools.refinery.viatra.runtime.base.itc.igraph; - -/** - * Interface ITcObserver is used to observe the changes in a transitive closure relation; tuple insertion/deletion. - * - * @author Szabo Tamas - * - */ -public interface ITcObserver { - - /** - * Used to notify when a tuple is inserted into the transitive closure relation. - * - * @param source - * the source of the tuple - * @param target - * the target of the tuple - */ - public void tupleInserted(V source, V target); - - /** - * Used to notify when a tuple is deleted from the transitive closure relation. - * - * @param source - * the source of the tuple - * @param target - * the target of the tuple - */ - public void tupleDeleted(V source, V target); -} -- cgit v1.2.3-54-g00ecf From b7a46b805bd7fbb3b21a48a035698ab11fadcb7c Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sat, 19 Aug 2023 14:39:39 +0200 Subject: feat: interruptible VIATRA engine Reduce server load by introducing a timeout for semantics analysis. --- .../language/web/semantics/SemanticsService.java | 129 ++++------------- .../language/web/semantics/SemanticsWorker.java | 133 ++++++++++++++++++ .../web/xtext/server/TransactionExecutor.java | 46 ++++-- .../web/xtext/server/push/PushWebDocument.java | 13 +- .../xtext/server/push/PushWebDocumentProvider.java | 9 +- .../language/web/xtext/servlet/XtextWebSocket.java | 14 +- .../query/viatra/ViatraModelQueryBuilder.java | 9 +- .../internal/ViatraModelQueryAdapterImpl.java | 5 + .../internal/ViatraModelQueryBuilderImpl.java | 10 +- .../internal/ViatraModelQueryStoreAdapterImpl.java | 9 +- .../internal/context/RelationalRuntimeContext.java | 9 ++ .../viatra/runtime/rete/network/ReteContainer.java | 38 ++--- .../viatra/runtime/rete/network/StandardNode.java | 18 +-- .../refinery/viatra/runtime/CancellationToken.java | 13 ++ .../matchers/context/IQueryRuntimeContext.java | 156 +++++++++++---------- 15 files changed, 379 insertions(+), 232 deletions(-) create mode 100644 subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java create mode 100644 subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java (limited to 'subprojects/viatra-runtime-rete') diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java index 2495430e..39191162 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsService.java @@ -5,8 +5,6 @@ */ package tools.refinery.language.web.semantics; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; import com.google.inject.Inject; import com.google.inject.Provider; import com.google.inject.Singleton; @@ -14,43 +12,29 @@ import org.eclipse.xtext.service.OperationCanceledManager; import org.eclipse.xtext.util.CancelIndicator; import org.eclipse.xtext.web.server.model.AbstractCachedService; import org.eclipse.xtext.web.server.model.IXtextWebDocument; -import org.eclipse.xtext.web.server.model.XtextWebDocument; import org.eclipse.xtext.web.server.validation.ValidationService; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tools.refinery.language.model.problem.Problem; -import tools.refinery.language.semantics.model.ModelInitializer; -import tools.refinery.language.semantics.model.SemanticsUtils; -import tools.refinery.store.model.Model; -import tools.refinery.store.model.ModelStore; -import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; -import tools.refinery.store.reasoning.ReasoningAdapter; -import tools.refinery.store.reasoning.ReasoningStoreAdapter; -import tools.refinery.store.reasoning.literal.Concreteness; -import tools.refinery.store.reasoning.representation.PartialRelation; -import tools.refinery.store.representation.TruthValue; -import tools.refinery.store.tuple.Tuple; +import tools.refinery.language.web.xtext.server.push.PushWebDocument; -import java.util.Arrays; -import java.util.List; -import java.util.TreeMap; +import java.util.concurrent.*; @Singleton public class SemanticsService extends AbstractCachedService { private static final Logger LOG = LoggerFactory.getLogger(SemanticsService.class); @Inject - private SemanticsUtils semanticsUtils; + private Provider workerProvider; @Inject - private ValidationService validationService; + private OperationCanceledManager operationCanceledManager; @Inject - private Provider initializerProvider; + private ValidationService validationService; - @Inject - private OperationCanceledManager operationCanceledManager; + private final ExecutorService executorService = Executors.newCachedThreadPool(); @Override public SemanticsResult compute(IXtextWebDocument doc, CancelIndicator cancelIndicator) { @@ -58,44 +42,42 @@ public class SemanticsService extends AbstractCachedService { if (LOG.isTraceEnabled()) { start = System.currentTimeMillis(); } - Problem problem = getProblem(doc, cancelIndicator); + var problem = getProblem(doc, cancelIndicator); if (problem == null) { return null; } - var initializer = initializerProvider.get(); - var builder = ModelStore.builder() - .with(ViatraModelQueryAdapter.builder()) - .with(ReasoningAdapter.builder() - .requiredInterpretations(Concreteness.PARTIAL)); - operationCanceledManager.checkCanceled(cancelIndicator); + var worker = workerProvider.get(); + worker.setProblem(problem,cancelIndicator); + var future = executorService.submit(worker); + SemanticsResult result = null; try { - var modelSeed = initializer.createModel(problem, builder); - operationCanceledManager.checkCanceled(cancelIndicator); - var nodeTrace = getNodeTrace(initializer); - operationCanceledManager.checkCanceled(cancelIndicator); - var store = builder.build(); - operationCanceledManager.checkCanceled(cancelIndicator); - var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); - operationCanceledManager.checkCanceled(cancelIndicator); - var partialInterpretation = getPartialInterpretation(initializer, model, cancelIndicator); - if (LOG.isTraceEnabled()) { - long end = System.currentTimeMillis(); - LOG.trace("Computed semantics for {} ({}) in {}ms", doc.getResourceId(), doc.getStateId(), - end - start); - } - return new SemanticsSuccessResult(nodeTrace, partialInterpretation); - } catch (RuntimeException e) { - LOG.debug("Error while computing semantics", e); - return new SemanticsErrorResult(e.getMessage()); + result = future.get(2, TimeUnit.SECONDS); + } catch (InterruptedException e) { + future.cancel(true); + LOG.error("Semantics service interrupted", e); + Thread.currentThread().interrupt(); + } catch (ExecutionException e) { + operationCanceledManager.propagateAsErrorIfCancelException(e.getCause()); + throw new IllegalStateException(e); + } catch (TimeoutException e) { + future.cancel(true); + LOG.trace("Semantics service timeout", e); + return new SemanticsErrorResult("Partial interpretation timed out"); } + if (LOG.isTraceEnabled()) { + long end = System.currentTimeMillis(); + LOG.trace("Computed semantics for {} ({}) in {}ms", doc.getResourceId(), doc.getStateId(), + end - start); + } + return result; } @Nullable private Problem getProblem(IXtextWebDocument doc, CancelIndicator cancelIndicator) { - if (!(doc instanceof XtextWebDocument webDoc)) { + if (!(doc instanceof PushWebDocument pushDoc)) { throw new IllegalArgumentException("Unexpected IXtextWebDocument: " + doc); } - var validationResult = webDoc.getCachedServiceResult(validationService, cancelIndicator, true); + var validationResult = pushDoc.getCachedServiceResult(validationService, cancelIndicator, true); boolean hasError = validationResult.getIssues().stream() .anyMatch(issue -> "error".equals(issue.getSeverity())); if (hasError) { @@ -111,53 +93,4 @@ public class SemanticsService extends AbstractCachedService { } return problem; } - - private List getNodeTrace(ModelInitializer initializer) { - var nodeTrace = new String[initializer.getNodeCount()]; - for (var entry : initializer.getNodeTrace().keyValuesView()) { - var node = entry.getOne(); - var index = entry.getTwo(); - nodeTrace[index] = semanticsUtils.getName(node).orElse(null); - } - return Arrays.asList(nodeTrace); - } - - private JsonObject getPartialInterpretation(ModelInitializer initializer, Model model, - CancelIndicator cancelIndicator) { - var adapter = model.getAdapter(ReasoningAdapter.class); - var json = new JsonObject(); - for (var entry : initializer.getRelationTrace().entrySet()) { - var relation = entry.getKey(); - var partialSymbol = entry.getValue(); - var tuples = getTuplesJson(adapter, partialSymbol); - var name = semanticsUtils.getName(relation).orElse(partialSymbol.name()); - json.add(name, tuples); - operationCanceledManager.checkCanceled(cancelIndicator); - } - return json; - } - - private static JsonArray getTuplesJson(ReasoningAdapter adapter, PartialRelation partialSymbol) { - var interpretation = adapter.getPartialInterpretation(Concreteness.PARTIAL, partialSymbol); - var cursor = interpretation.getAll(); - var map = new TreeMap(); - while (cursor.move()) { - map.put(cursor.getKey(), cursor.getValue()); - } - var tuples = new JsonArray(); - for (var entry : map.entrySet()) { - tuples.add(toArray(entry.getKey(), entry.getValue())); - } - return tuples; - } - - private static JsonArray toArray(Tuple tuple, TruthValue value) { - int arity = tuple.getSize(); - var json = new JsonArray(arity + 1); - for (int i = 0; i < arity; i++) { - json.add(tuple.get(i)); - } - json.add(value.toString()); - return json; - } } diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java new file mode 100644 index 00000000..25589260 --- /dev/null +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/semantics/SemanticsWorker.java @@ -0,0 +1,133 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.language.web.semantics; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import com.google.inject.Inject; +import org.eclipse.xtext.service.OperationCanceledManager; +import org.eclipse.xtext.util.CancelIndicator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tools.refinery.language.model.problem.Problem; +import tools.refinery.language.semantics.model.ModelInitializer; +import tools.refinery.language.semantics.model.SemanticsUtils; +import tools.refinery.store.model.Model; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.reasoning.ReasoningAdapter; +import tools.refinery.store.reasoning.ReasoningStoreAdapter; +import tools.refinery.store.reasoning.literal.Concreteness; +import tools.refinery.store.reasoning.representation.PartialRelation; +import tools.refinery.store.representation.TruthValue; +import tools.refinery.store.tuple.Tuple; +import tools.refinery.viatra.runtime.CancellationToken; + +import java.util.Arrays; +import java.util.List; +import java.util.TreeMap; +import java.util.concurrent.Callable; + +class SemanticsWorker implements Callable { + private static final Logger LOG = LoggerFactory.getLogger(SemanticsWorker.class); + + @Inject + private SemanticsUtils semanticsUtils; + + @Inject + private OperationCanceledManager operationCanceledManager; + + @Inject + private ModelInitializer initializer; + + private Problem problem; + + private CancellationToken cancellationToken; + + public void setProblem(Problem problem, CancelIndicator parentIndicator) { + this.problem = problem; + cancellationToken = () -> { + if (Thread.interrupted() || parentIndicator.isCanceled()) { + operationCanceledManager.throwOperationCanceledException(); + } + }; + } + + @Override + public SemanticsResult call() { + var builder = ModelStore.builder() + .with(ViatraModelQueryAdapter.builder() + .cancellationToken(cancellationToken)) + .with(ReasoningAdapter.builder() + .requiredInterpretations(Concreteness.PARTIAL)); + cancellationToken.checkCancelled(); + try { + var modelSeed = initializer.createModel(problem, builder); + cancellationToken.checkCancelled(); + var nodeTrace = getNodeTrace(initializer); + cancellationToken.checkCancelled(); + var store = builder.build(); + cancellationToken.checkCancelled(); + var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); + cancellationToken.checkCancelled(); + var partialInterpretation = getPartialInterpretation(initializer, model); + + return new SemanticsSuccessResult(nodeTrace, partialInterpretation); + } catch (RuntimeException e) { + LOG.debug("Error while computing semantics", e); + var message = e.getMessage(); + return new SemanticsErrorResult(message == null ? "Partial interpretation error" : e.getMessage()); + } + } + + private List getNodeTrace(ModelInitializer initializer) { + var nodeTrace = new String[initializer.getNodeCount()]; + for (var entry : initializer.getNodeTrace().keyValuesView()) { + var node = entry.getOne(); + var index = entry.getTwo(); + nodeTrace[index] = semanticsUtils.getName(node).orElse(null); + } + return Arrays.asList(nodeTrace); + } + + private JsonObject getPartialInterpretation(ModelInitializer initializer, Model model) { + var adapter = model.getAdapter(ReasoningAdapter.class); + var json = new JsonObject(); + for (var entry : initializer.getRelationTrace().entrySet()) { + var relation = entry.getKey(); + var partialSymbol = entry.getValue(); + var tuples = getTuplesJson(adapter, partialSymbol); + var name = semanticsUtils.getName(relation).orElse(partialSymbol.name()); + json.add(name, tuples); + cancellationToken.checkCancelled(); + } + return json; + } + + private static JsonArray getTuplesJson(ReasoningAdapter adapter, PartialRelation partialSymbol) { + var interpretation = adapter.getPartialInterpretation(Concreteness.PARTIAL, partialSymbol); + var cursor = interpretation.getAll(); + var map = new TreeMap(); + while (cursor.move()) { + map.put(cursor.getKey(), cursor.getValue()); + } + var tuples = new JsonArray(); + for (var entry : map.entrySet()) { + tuples.add(toArray(entry.getKey(), entry.getValue())); + } + return tuples; + } + + private static JsonArray toArray(Tuple tuple, TruthValue value) { + int arity = tuple.getSize(); + var json = new JsonArray(arity + 1); + for (int i = 0; i < arity; i++) { + json.add(tuple.get(i)); + } + json.add(value.toString()); + return json; + } +} diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java index 2c0e9329..74456604 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/TransactionExecutor.java @@ -42,6 +42,8 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener private final List pendingPushMessages = new ArrayList<>(); + private volatile boolean disposed; + public TransactionExecutor(ISession session, IResourceServiceProvider.Registry resourceServiceProviderRegistry) { this.session = session; this.resourceServiceProviderRegistry = resourceServiceProviderRegistry; @@ -52,10 +54,13 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener } public void handleRequest(XtextWebRequest request) throws ResponseHandlerException { + if (disposed) { + return; + } var serviceContext = new SimpleServiceContext(session, request.getRequestData()); var ping = serviceContext.getParameter("ping"); if (ping != null) { - responseHandler.onResponse(new XtextWebOkResponse(request, new PongResult(ping))); + onResponse(new XtextWebOkResponse(request, new PongResult(ping))); return; } synchronized (callPendingLock) { @@ -72,23 +77,36 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener var serviceDispatcher = injector.getInstance(XtextServiceDispatcher.class); var service = serviceDispatcher.getService(new SubscribingServiceContext(serviceContext, this)); var serviceResult = service.getService().apply(); - responseHandler.onResponse(new XtextWebOkResponse(request, serviceResult)); + onResponse(new XtextWebOkResponse(request, serviceResult)); } catch (InvalidRequestException e) { - responseHandler.onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.REQUEST_ERROR, e)); + onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.REQUEST_ERROR, e)); } catch (RuntimeException e) { - responseHandler.onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.SERVER_ERROR, e)); + onResponse(new XtextWebErrorResponse(request, XtextWebErrorKind.SERVER_ERROR, e)); } finally { - synchronized (callPendingLock) { - for (var message : pendingPushMessages) { - try { - responseHandler.onResponse(message); - } catch (ResponseHandlerException | RuntimeException e) { - LOG.error("Error while flushing push message", e); - } + flushPendingPushMessages(); + } + } + + private void onResponse(XtextWebResponse response) throws ResponseHandlerException { + if (!disposed) { + responseHandler.onResponse(response); + } + } + + private void flushPendingPushMessages() { + synchronized (callPendingLock) { + for (var message : pendingPushMessages) { + if (disposed) { + return; + } + try { + responseHandler.onResponse(message); + } catch (ResponseHandlerException | RuntimeException e) { + LOG.error("Error while flushing push message", e); } - pendingPushMessages.clear(); - callPending = false; } + pendingPushMessages.clear(); + callPending = false; } } @@ -164,10 +182,12 @@ public class TransactionExecutor implements IDisposable, PrecomputationListener @Override public void dispose() { + disposed = true; for (var subscription : subscriptions.values()) { var document = subscription.get(); if (document != null) { document.removePrecomputationListener(this); + document.cancelBackgroundWork(); } } } diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java index dfbd4878..1542c694 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocument.java @@ -27,11 +27,11 @@ public class PushWebDocument extends XtextWebDocument { private final Map, IServiceResult> precomputedServices = new HashMap<>(); + private final DocumentSynchronizer synchronizer; + public PushWebDocument(String resourceId, DocumentSynchronizer synchronizer) { super(resourceId, synchronizer); - if (resourceId == null) { - throw new IllegalArgumentException("resourceId must not be null"); - } + this.synchronizer = synchronizer; } public void addPrecomputationListener(PrecomputationListener listener) { @@ -63,6 +63,9 @@ public class PushWebDocument extends XtextWebDocument { private void notifyPrecomputationListeners(String serviceName, T result) { var resourceId = getResourceId(); + if (resourceId == null) { + return; + } var stateId = getStateId(); List copyOfListeners; synchronized (precomputationListeners) { @@ -83,4 +86,8 @@ public class PushWebDocument extends XtextWebDocument { } } } + + public void cancelBackgroundWork() { + synchronizer.setCanceled(true); + } } diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java index b6f4fb43..ec6204ef 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/server/push/PushWebDocumentProvider.java @@ -27,12 +27,7 @@ public class PushWebDocumentProvider implements IWebDocumentProvider { @Override public XtextWebDocument get(String resourceId, IServiceContext serviceContext) { - if (resourceId == null) { - return new XtextWebDocument(null, synchronizerProvider.get()); - } else { - // We only need to send push messages if a resourceId is specified. - return new PushWebDocument(resourceId, - serviceContext.getSession().get(DocumentSynchronizer.class, () -> this.synchronizerProvider.get())); - } + return new PushWebDocument(resourceId, + serviceContext.getSession().get(DocumentSynchronizer.class, () -> this.synchronizerProvider.get())); } } diff --git a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java index 043d318c..923fecd6 100644 --- a/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java +++ b/subprojects/language-web/src/main/java/tools/refinery/language/web/xtext/servlet/XtextWebSocket.java @@ -70,10 +70,11 @@ public class XtextWebSocket implements ResponseHandler { @OnWebSocketError public void onError(Throwable error) { + executor.dispose(); if (webSocketSession == null) { return; } - LOG.error("Internal websocket error in connection from" + webSocketSession.getRemoteSocketAddress(), error); + LOG.error("Internal websocket error in connection from " + webSocketSession.getRemoteSocketAddress(), error); } @OnWebSocketMessage @@ -86,14 +87,18 @@ public class XtextWebSocket implements ResponseHandler { try { request = gson.fromJson(reader, XtextWebRequest.class); } catch (JsonIOException e) { - LOG.error("Cannot read from websocket from" + webSocketSession.getRemoteSocketAddress(), e); + LOG.error("Cannot read from websocket from " + webSocketSession.getRemoteSocketAddress(), e); if (webSocketSession.isOpen()) { + executor.dispose(); webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot read payload", Callback.NOOP); } return; } catch (JsonParseException e) { - LOG.warn("Malformed websocket request from" + webSocketSession.getRemoteSocketAddress(), e); - webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload", Callback.NOOP); + LOG.warn("Malformed websocket request from " + webSocketSession.getRemoteSocketAddress(), e); + if (webSocketSession.isOpen()) { + executor.dispose(); + webSocketSession.close(XtextStatusCode.INVALID_JSON, "Invalid JSON payload", Callback.NOOP); + } return; } try { @@ -101,6 +106,7 @@ public class XtextWebSocket implements ResponseHandler { } catch (ResponseHandlerException e) { LOG.warn("Cannot write websocket response", e); if (webSocketSession.isOpen()) { + executor.dispose(); webSocketSession.close(StatusCode.SERVER_ERROR, "Cannot write response", Callback.NOOP); } } 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 d31325f1..6b3be115 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 @@ -5,14 +5,15 @@ */ package tools.refinery.store.query.viatra; -import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; -import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; -import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; import tools.refinery.store.model.ModelStore; import tools.refinery.store.query.ModelQueryBuilder; import tools.refinery.store.query.dnf.AnyQuery; import tools.refinery.store.query.dnf.Dnf; import tools.refinery.store.query.rewriter.DnfRewriter; +import tools.refinery.viatra.runtime.CancellationToken; +import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; +import tools.refinery.viatra.runtime.matchers.backend.IQueryBackendFactory; +import tools.refinery.viatra.runtime.matchers.backend.QueryEvaluationHint; import java.util.Collection; import java.util.function.Function; @@ -29,6 +30,8 @@ public interface ViatraModelQueryBuilder extends ModelQueryBuilder { ViatraModelQueryBuilder searchBackend(IQueryBackendFactory queryBackendFactory); + ViatraModelQueryBuilder cancellationToken(CancellationToken cancellationToken); + @Override default ViatraModelQueryBuilder queries(AnyQuery... queries) { ModelQueryBuilder.super.queries(queries); 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 f1209f69..ad754988 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 @@ -18,6 +18,7 @@ import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; import tools.refinery.store.query.viatra.internal.matcher.FunctionalViatraMatcher; import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; import tools.refinery.store.query.viatra.internal.matcher.RelationalViatraMatcher; +import tools.refinery.viatra.runtime.CancellationToken; import tools.refinery.viatra.runtime.api.AdvancedViatraQueryEngine; import tools.refinery.viatra.runtime.api.GenericQueryGroup; import tools.refinery.viatra.runtime.api.IQuerySpecification; @@ -81,6 +82,10 @@ public class ViatraModelQueryAdapterImpl implements ViatraModelQueryAdapter, Mod return storeAdapter; } + public CancellationToken getCancellationToken() { + return storeAdapter.getCancellationToken(); + } + @Override public ResultSet getResultSet(Query query) { var canonicalQuery = storeAdapter.getCanonicalQuery(query); 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 cfdc43ba..bb0630f3 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 @@ -17,6 +17,7 @@ import tools.refinery.store.query.viatra.ViatraModelQueryBuilder; import tools.refinery.store.query.viatra.internal.localsearch.FlatCostFunction; import tools.refinery.store.query.viatra.internal.matcher.RawPatternMatcher; import tools.refinery.store.query.viatra.internal.pquery.Dnf2PQuery; +import tools.refinery.viatra.runtime.CancellationToken; import tools.refinery.viatra.runtime.api.IQuerySpecification; import tools.refinery.viatra.runtime.api.ViatraQueryEngineOptions; import tools.refinery.viatra.runtime.localsearch.matcher.integration.LocalSearchGenericBackendFactory; @@ -35,6 +36,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder queries = new LinkedHashSet<>(); @@ -84,6 +86,12 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder queries) { checkNotConfigured(); @@ -136,7 +144,7 @@ public class ViatraModelQueryBuilderImpl extends AbstractModelAdapterBuilder> querySpecifications; private final Set vacuousQueries; private final Set allQueries; + private final CancellationToken cancellationToken; ViatraModelQueryStoreAdapterImpl(ModelStore store, ViatraQueryEngineOptions engineOptions, Map inputKeys, Map canonicalQueryMap, Map> querySpecifications, - Set vacuousQueries) { + Set vacuousQueries, CancellationToken cancellationToken) { this.store = store; this.engineOptions = engineOptions; this.inputKeys = inputKeys; this.canonicalQueryMap = canonicalQueryMap; this.querySpecifications = querySpecifications; this.vacuousQueries = vacuousQueries; + this.cancellationToken = cancellationToken; var mutableAllQueries = new LinkedHashSet(querySpecifications.size() + vacuousQueries.size()); mutableAllQueries.addAll(querySpecifications.keySet()); mutableAllQueries.addAll(vacuousQueries); @@ -62,6 +65,10 @@ public class ViatraModelQueryStoreAdapterImpl implements ViatraModelQueryStoreAd return allQueries; } + public CancellationToken getCancellationToken() { + return cancellationToken; + } + @Override public Query getCanonicalQuery(Query query) { // We know that canonical forms of queries do not change output types. 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 d1fa5239..dadab5dd 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 @@ -5,6 +5,7 @@ */ package tools.refinery.store.query.viatra.internal.context; +import tools.refinery.viatra.runtime.CancellationToken; import tools.refinery.viatra.runtime.matchers.context.*; import tools.refinery.viatra.runtime.matchers.tuple.ITuple; import tools.refinery.viatra.runtime.matchers.tuple.Tuple; @@ -32,10 +33,13 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext { private final Model model; + private final CancellationToken cancellationToken; + RelationalRuntimeContext(ViatraModelQueryAdapterImpl adapter) { model = adapter.getModel(); metaContext = new RelationalQueryMetaContext(adapter.getStoreAdapter().getInputKeys()); modelUpdateListener = new ModelUpdateListener(adapter); + cancellationToken = adapter.getCancellationToken(); } @Override @@ -192,4 +196,9 @@ public class RelationalRuntimeContext implements IQueryRuntimeContext { public void executeAfterTraversal(Runnable runnable) { runnable.run(); } + + @Override + public CancellationToken getCancellationToken() { + return cancellationToken; + } } diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java index 16e290fd..79e0526d 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/ReteContainer.java @@ -1,26 +1,17 @@ /******************************************************************************* * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. - * + * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package tools.refinery.viatra.runtime.rete.network; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Deque; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.LinkedList; -import java.util.Map; -import java.util.Set; -import java.util.function.Function; - import org.apache.log4j.Logger; +import tools.refinery.viatra.runtime.CancellationToken; import tools.refinery.viatra.runtime.matchers.context.IQueryBackendContext; import tools.refinery.viatra.runtime.matchers.tuple.Tuple; import tools.refinery.viatra.runtime.matchers.util.Clearable; @@ -42,6 +33,9 @@ import tools.refinery.viatra.runtime.rete.single.SingleInputNode; import tools.refinery.viatra.runtime.rete.single.TrimmerNode; import tools.refinery.viatra.runtime.rete.util.Options; +import java.util.*; +import java.util.function.Function; + /** * @author Gabor Bergmann * @@ -79,6 +73,8 @@ public final class ReteContainer { protected final TimelyConfiguration timelyConfiguration; + private final CancellationToken cancellationToken; + /** * @param threaded * false if operating in a single-threaded environment @@ -88,6 +84,7 @@ public final class ReteContainer { this.network = network; this.backendContext = network.getEngine().getBackendContext(); this.timelyConfiguration = network.getEngine().getTimelyConfiguration(); + cancellationToken = backendContext.getRuntimeContext().getCancellationToken(); this.delayedCommandQueue = new LinkedHashSet(); this.delayedCommandBuffer = new LinkedHashSet(); @@ -395,10 +392,10 @@ public final class ReteContainer { /** * Retrieves a safe copy of the contents of a supplier. - * + * *

Note that there may be multiple copies of a Tuple in case of a {@link TrimmerNode}, so the result is not always a set. - * - * @param flush if true, a flush is performed before pulling the contents + * + * @param flush if true, a flush is performed before pulling the contents * @since 2.3 */ public Collection pullContents(final Supplier supplier, final boolean flush) { @@ -424,7 +421,7 @@ public final class ReteContainer { /** * Retrieves the contents of a SingleInputNode's parentage. - * + * * @since 2.3 */ public Collection pullPropagatedContents(final SingleInputNode supplier, final boolean flush) { @@ -438,7 +435,7 @@ public final class ReteContainer { /** * Retrieves the timestamp-aware contents of a SingleInputNode's parentage. - * + * * @since 2.3 */ public Map> pullPropagatedContentsWithTimestamp(final SingleInputNode supplier, @@ -541,7 +538,7 @@ public final class ReteContainer { /** * Sends out all pending messages to their receivers. The delivery is governed by the communication tracker. - * + * * @since 1.6 */ public void deliverMessagesSingleThreaded() { @@ -620,7 +617,7 @@ public final class ReteContainer { /** * Returns an addressed node at this container. - * + * * @pre: address.container == this, e.g. address MUST be local * @throws IllegalArgumentException * if address is non-local @@ -726,4 +723,7 @@ public final class ReteContainer { return network.getInputConnector(); } + public void checkCancelled() { + cancellationToken.checkCancelled(); + } } diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java index e7ec36dc..7dc7c4bc 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/network/StandardNode.java @@ -1,19 +1,15 @@ /******************************************************************************* * Copyright (c) 2004-2008 Gabor Bergmann and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. - * + * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package tools.refinery.viatra.runtime.rete.network; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - import tools.refinery.viatra.runtime.matchers.tuple.Tuple; import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; @@ -24,11 +20,16 @@ import tools.refinery.viatra.runtime.rete.network.communication.Timestamp; import tools.refinery.viatra.runtime.rete.network.mailbox.Mailbox; import tools.refinery.viatra.runtime.rete.traceability.TraceInfo; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + /** * Base implementation for a supplier node. - * + * * @author Gabor Bergmann - * + * */ public abstract class StandardNode extends BaseNode implements Supplier, NetworkStructureChangeSensitiveNode { protected final List children = CollectionsFactory.createObserverList(); @@ -45,6 +46,7 @@ public abstract class StandardNode extends BaseNode implements Supplier, Network * @since 2.4 */ protected void propagateUpdate(final Direction direction, final Tuple updateElement, final Timestamp timestamp) { + reteContainer.checkCancelled(); for (final Mailbox childMailbox : childMailboxes) { childMailbox.postMessage(direction, updateElement, timestamp); } diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java new file mode 100644 index 00000000..a2ae41e3 --- /dev/null +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/CancellationToken.java @@ -0,0 +1,13 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.viatra.runtime; + +@FunctionalInterface +public interface CancellationToken { + CancellationToken NONE = () -> {}; + + void checkCancelled(); +} diff --git a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java index c2e90614..61359c1b 100644 --- a/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java +++ b/subprojects/viatra-runtime/src/main/java/tools/refinery/viatra/runtime/matchers/context/IQueryRuntimeContext.java @@ -1,82 +1,84 @@ /******************************************************************************* * Copyright (c) 2010-2015, Bergmann Gabor, Istvan Rath and Daniel Varro + * Copyright (c) 2023 The Refinery Authors * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0 which is available at * http://www.eclipse.org/legal/epl-v20.html. - * + * * SPDX-License-Identifier: EPL-2.0 *******************************************************************************/ package tools.refinery.viatra.runtime.matchers.context; -import java.lang.reflect.InvocationTargetException; -import java.util.Optional; -import java.util.concurrent.Callable; - +import tools.refinery.viatra.runtime.CancellationToken; import tools.refinery.viatra.runtime.matchers.planning.helpers.StatisticsHelper; import tools.refinery.viatra.runtime.matchers.tuple.ITuple; import tools.refinery.viatra.runtime.matchers.tuple.Tuple; import tools.refinery.viatra.runtime.matchers.tuple.TupleMask; import tools.refinery.viatra.runtime.matchers.util.Accuracy; +import java.lang.reflect.InvocationTargetException; +import java.util.Optional; +import java.util.concurrent.Callable; + /** * Provides instance model information (relations corresponding to input keys) to query evaluator backends at runtime. * Implementors shall extend {@link AbstractQueryRuntimeContext} instead directly this interface. - * + * * @author Bergmann Gabor * @noimplement This interface is not intended to be implemented by clients. Extend {@link AbstractQueryRuntimeContext} instead. */ public interface IQueryRuntimeContext { - /** + /** * Provides metamodel-specific info independent of the runtime instance model. */ public IQueryMetaContext getMetaContext(); - - + + /** * The given callable will be executed, and all model traversals will be delayed until the execution is done. If * there are any outstanding information to be read from the model, a single coalesced model traversal will * initialize the caches and deliver the notifications. - * + * *

Calls may be nested. A single coalesced traversal will happen at the end of the outermost call. - * - *

Caution: results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. - * For example, if a certain input key is not cached yet, an empty relation may be reported during callable.call(); the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. + * + *

Caution: results returned by the runtime context may be incomplete during the coalescing period, to be corrected by notifications sent during the final coalesced traversal. + * For example, if a certain input key is not cached yet, an empty relation may be reported during callable.call(); the cache will be constructed after the call terminates and notifications will deliver the entire content of the relation. * Non-incremental query backends should therefore never enumerate input keys while coalesced (verify using {@link #isCoalescing()}). - * + * * @param callable */ - public abstract V coalesceTraversals(Callable callable) throws InvocationTargetException; + public abstract V coalesceTraversals(Callable callable) throws InvocationTargetException; /** * @return true iff currently within a coalescing section (i.e. within the callable of a call to {@link #coalesceTraversals(Callable)}). */ public boolean isCoalescing(); - + /** * Returns true if index is available for the given key providing the given service. * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @since 1.4 */ public boolean isIndexed(IInputKey key, IndexingService service); - + /** - * If the given (enumerable) input key is not yet indexed, the model will be traversed - * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) + * If the given (enumerable) input key is not yet indexed, the model will be traversed + * (after the end of the outermost coalescing block, see {@link IQueryRuntimeContext#coalesceTraversals(Callable)}) * so that the index can be built. It is possible that the base indexer will select a higher indexing level merging * multiple indexing requests to an appropriate level. - * + * *

Postcondition: After invoking this method, {@link #getIndexed(IInputKey, IndexingService)} for the same key * and service will be guaranteed to return the requested or a highing indexing level as soon as {@link #isCoalescing()} first returns false. - * + * *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException if key is not enumerable or an unknown type, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @since 1.4 */ public void ensureIndexed(IInputKey key, IndexingService service); - + /** * Returns the number of tuples in the extensional relation identified by the input key seeded with the given mask and tuple. - * - * @param key an input key + * + * @param key an input key * @param seedMask * a mask that extracts those parameters of the input key (from the entire parameter list) that should be * bound to a fixed value; must not be null. Note: any given index must occur at most once in seedMask. @@ -84,59 +86,59 @@ public interface IQueryRuntimeContext { * the tuple of fixed values restricting the match set to be considered, in the same order as given in * parameterSeedMask, so that for each considered match tuple, * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. - * + * * @return the number of tuples in the model for the given key and seed - * + * *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @since 1.7 */ public int countTuples(IInputKey key, TupleMask seedMask, ITuple seed); - - + + /** * Gives an estimate of the number of different groups the tuples of the given relation are projected into by the given mask * (e.g. for an identity mask, this means the full relation size). The estimate must meet the required accuracy. - * - *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. - * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. - * + * + *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. + * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} is returned. + * *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. - * + * * @return if available, an estimate of the cardinality of the projection of the given extensional relation, with the desired accuracy. - * + * * @since 2.1 */ public Optional estimateCardinality(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy); - - + + /** * Gives an estimate of the average size of different groups the tuples of the given relation are projected into by the given mask - * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). + * (e.g. for an identity mask, this means 1, while for an empty mask, the result is the full relation size). * The estimate must meet the required accuracy. - * - *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. + * + *

Must accept any input key, even non-enumerables or those not recognized by this runtime context. * If there is insufficient information to provide an answer up to the required precision, {@link Optional#empty()} may be returned. - * + * *

For an empty relation, zero is acceptable as an exact answer. - * + * *

PRE: {@link TupleMask#isNonrepeating()} must hold for the group mask. - * + * * @return if available, an estimate of the average size of each projection group of the given extensional relation, with the desired accuracy. - * + * * @since 2.1 */ public default Optional estimateAverageBucketSize(IInputKey key, TupleMask groupMask, Accuracy requiredAccuracy) { if (key.isEnumerable()) { - return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, + return StatisticsHelper.estimateAverageBucketSize(groupMask, requiredAccuracy, (mask, accuracy) -> this.estimateCardinality(key, mask, accuracy)); } else return groupMask.isIdentity() ? Optional.of(1.0) : Optional.empty(); } - - + + /** * Returns the tuples in the extensional relation identified by the input key, optionally seeded with the given tuple. - * + * * @param key an input key * @param seedMask * a mask that extracts those parameters of the input key (from the entire parameter list) that should be @@ -144,23 +146,23 @@ public interface IQueryRuntimeContext { * @param seed * the tuple of fixed values restricting the match set to be considered, in the same order as given in * parameterSeedMask, so that for each considered match tuple, - * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. + * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. * @return the tuples in the model for the given key and seed - * + * *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @since 1.7 */ public Iterable enumerateTuples(IInputKey key, TupleMask seedMask, ITuple seed); - + /** * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples * are bound by the seed except for one. - * + * *

* Selects the tuples in the extensional relation identified by the input key, optionally seeded with the given * tuple, and then returns the single value from each tuple which is not bound by the ssed mask. - * + * * @param key * an input key * @param seedMask @@ -172,7 +174,7 @@ public interface IQueryRuntimeContext { * parameterSeedMask, so that for each considered match tuple, * projectedParameterSeed.equals(parameterSeedMask.transform(match)) should hold. Must not be null. * @return the objects in the model for the given key and seed - * + * *

* Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException @@ -180,17 +182,17 @@ public interface IQueryRuntimeContext { * @since 1.7 */ public Iterable enumerateValues(IInputKey key, TupleMask seedMask, ITuple seed); - + /** * Simpler form of {@link #enumerateTuples(IInputKey, TupleMask, Tuple)} in the case where all values of the tuples * are bound by the seed. - * + * *

* Returns whether the given tuple is in the extensional relation identified by the input key. - * + * *

* Note: this call works for non-enumerable input keys as well. - * + * * @param key * an input key * @param seed @@ -202,31 +204,31 @@ public interface IQueryRuntimeContext { */ public boolean containsTuple(IInputKey key, ITuple seed); - + /** * Subscribes for updates in the extensional relation identified by the input key, optionally seeded with the given tuple. - *

This should be called after invoking - * + *

This should be called after invoking + * * @param key an input key - * @param seed can be null or a tuple with matching arity; - * if non-null, only those updates in the model are notified about - * that match the seed at positions where the seed is non-null. + * @param seed can be null or a tuple with matching arity; + * if non-null, only those updates in the model are notified about + * that match the seed at positions where the seed is non-null. * @param listener will be notified of future changes - * + * *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. */ public void addUpdateListener(IInputKey key, Tuple seed, IQueryRuntimeContextListener listener); - + /** * Unsubscribes from updates in the extensional relation identified by the input key, optionally seeded with the given tuple. - * + * * @param key an input key - * @param seed can be null or a tuple with matching arity; - * if non-null, only those updates in the model are notified about - * that match the seed at positions where the seed is non-null. + * @param seed can be null or a tuple with matching arity; + * if non-null, only those updates in the model are notified about + * that match the seed at positions where the seed is non-null. * @param listener will no longer be notified of future changes - * + * *

Precondition: the given key is enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. * @throws IllegalArgumentException if key is not enumerable, see {@link IQueryMetaContext#isEnumerable(IInputKey)}. */ @@ -234,16 +236,16 @@ public interface IQueryRuntimeContext { /* TODO: uniqueness */ - + /** - * Wraps the external element into the internal representation that is to be used by the query backend + * Wraps the external element into the internal representation that is to be used by the query backend *

model element -> internal object. *

null must be mapped to null. */ public Object wrapElement(Object externalElement); /** - * Unwraps the internal representation of the element into its original form + * Unwraps the internal representation of the element into its original form *

internal object -> model element *

null must be mapped to null. */ @@ -269,13 +271,17 @@ public interface IQueryRuntimeContext { * @since 1.4 */ public void ensureWildcardIndexing(IndexingService service); - + /** * Execute the given runnable after traversal. It is guaranteed that the runnable is executed as soon as * the indexing is finished. The callback is executed only once, then is removed from the callback queue. * @param traversalCallback - * @throws InvocationTargetException + * @throws InvocationTargetException * @since 1.4 */ public void executeAfterTraversal(Runnable runnable) throws InvocationTargetException; + + default CancellationToken getCancellationToken() { + return CancellationToken.NONE; + } } -- cgit v1.2.3-54-g00ecf From cca3b176cea8df8823094c105f612844b649647e Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Sun, 27 Aug 2023 02:13:15 +0200 Subject: fix: strong represenative election algorithm Make sure to merge all clusters reachable from source and target. --- .../viatra/StronglyConnectedComponentsTest.java | 44 +++++++ .../ContainmentHierarchyTranslatorTest.java | 128 +++++++++++++++++++++ .../viatra/runtime/rete/itc/alg/misc/bfs/BFS.java | 29 +++-- .../RepresentativeElectionAlgorithm.java | 34 ++++++ .../StronglyConnectedComponentAlgorithm.java | 5 +- 5 files changed, 223 insertions(+), 17 deletions(-) create mode 100644 subprojects/store-reasoning/src/test/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslatorTest.java (limited to 'subprojects/viatra-runtime-rete') diff --git a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/StronglyConnectedComponentsTest.java b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/StronglyConnectedComponentsTest.java index 05ea1bbb..37795ff3 100644 --- a/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/StronglyConnectedComponentsTest.java +++ b/subprojects/store-query-viatra/src/test/java/tools/refinery/store/query/viatra/StronglyConnectedComponentsTest.java @@ -214,4 +214,48 @@ class StronglyConnectedComponentsTest { assertThat(resultSet.size(), is(2)); assertThat(resultSet.get(Tuple.of(2)), is(true)); } + + @Test + void loopTest() { + var query = Query.of("SymbolViewRepresentative", (builder, p1, p2) -> builder + .clause(v1 -> List.of( + new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p1, v1), + new RepresentativeElectionLiteral(Connectivity.STRONG, friendView, p2, v1) + ))); + + var store = ModelStore.builder() + .symbols(friend) + .with(ViatraModelQueryAdapter.builder() + .queries(query)) + .build(); + + var model = store.createEmptyModel(); + var friendInterpretation = model.getInterpretation(friend); + var queryEngine = model.getAdapter(ModelQueryAdapter.class); + var resultSet = queryEngine.getResultSet(query); + + friendInterpretation.put(Tuple.of(0, 1), true); + friendInterpretation.put(Tuple.of(1, 2), true); + friendInterpretation.put(Tuple.of(2, 3), true); + friendInterpretation.put(Tuple.of(3, 0), true); + friendInterpretation.put(Tuple.of(3, 4), true); + queryEngine.flushChanges(); + + assertThat(resultSet.get(Tuple.of(0, 1)), is(true)); + assertThat(resultSet.get(Tuple.of(1, 2)), is(true)); + assertThat(resultSet.get(Tuple.of(2, 3)), is(true)); + assertThat(resultSet.get(Tuple.of(3, 0)), is(true)); + assertThat(resultSet.get(Tuple.of(3, 4)), is(false)); + + friendInterpretation.put(Tuple.of(2, 3), false); + queryEngine.flushChanges(); + + assertThat(resultSet.get(Tuple.of(0, 1)), is(false)); + assertThat(resultSet.get(Tuple.of(0, 2)), is(false)); + assertThat(resultSet.get(Tuple.of(0, 3)), is(false)); + assertThat(resultSet.get(Tuple.of(1, 2)), is(false)); + assertThat(resultSet.get(Tuple.of(2, 3)), is(false)); + assertThat(resultSet.get(Tuple.of(3, 0)), is(false)); + assertThat(resultSet.get(Tuple.of(3, 4)), is(false)); + } } diff --git a/subprojects/store-reasoning/src/test/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslatorTest.java b/subprojects/store-reasoning/src/test/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslatorTest.java new file mode 100644 index 00000000..bbfaff84 --- /dev/null +++ b/subprojects/store-reasoning/src/test/java/tools/refinery/store/reasoning/translator/containment/ContainmentHierarchyTranslatorTest.java @@ -0,0 +1,128 @@ +/* + * SPDX-FileCopyrightText: 2023 The Refinery Authors + * + * SPDX-License-Identifier: EPL-2.0 + */ +package tools.refinery.store.reasoning.translator.containment; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import tools.refinery.store.model.ModelStore; +import tools.refinery.store.query.viatra.ViatraModelQueryAdapter; +import tools.refinery.store.reasoning.ReasoningAdapter; +import tools.refinery.store.reasoning.ReasoningStoreAdapter; +import tools.refinery.store.reasoning.literal.Concreteness; +import tools.refinery.store.reasoning.representation.PartialRelation; +import tools.refinery.store.reasoning.seed.ModelSeed; +import tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator; +import tools.refinery.store.reasoning.translator.multiplicity.UnconstrainedMultiplicity; +import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchy; +import tools.refinery.store.reasoning.translator.typehierarchy.TypeHierarchyTranslator; +import tools.refinery.store.representation.TruthValue; +import tools.refinery.store.representation.cardinality.CardinalityIntervals; +import tools.refinery.store.tuple.Tuple; + +import java.util.Map; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static tools.refinery.store.reasoning.translator.containment.ContainmentHierarchyTranslator.CONTAINED_SYMBOL; +import static tools.refinery.store.reasoning.translator.containment.ContainmentHierarchyTranslator.CONTAINS_SYMBOL; +import static tools.refinery.store.reasoning.translator.multiobject.MultiObjectTranslator.COUNT_SYMBOL; + +class ContainmentHierarchyTranslatorTest { + private final PartialRelation c1 = new PartialRelation("C1", 1); + private final PartialRelation c2 = new PartialRelation("C2", 1); + private final PartialRelation entry = new PartialRelation("entry", 2); + + private ModelStore store; + + @BeforeEach + void beforeEach() { + + var typeHierarchy = TypeHierarchy.builder() + .type(CONTAINED_SYMBOL, true) + .type(c1) + .type(c2, c1, CONTAINED_SYMBOL) + .build(); + + var containmentHierarchy = Map.of( + entry, + new ContainmentInfo(c1, UnconstrainedMultiplicity.INSTANCE, c2) + ); + + store = ModelStore.builder() + .with(ViatraModelQueryAdapter.builder()) + .with(ReasoningAdapter.builder()) + .with(new MultiObjectTranslator()) + .with(new TypeHierarchyTranslator(typeHierarchy)) + .with(new ContainmentHierarchyTranslator(containmentHierarchy)) + .build(); + } + + @Test + void treeTest() { + var modelSeed = ModelSeed.builder(3) + .seed(COUNT_SYMBOL, builder -> builder.reducedValue(CardinalityIntervals.ONE)) + .seed(CONTAINED_SYMBOL, builder -> builder.reducedValue(TruthValue.UNKNOWN)) + .seed(CONTAINS_SYMBOL, builder -> builder.reducedValue(TruthValue.UNKNOWN)) + .seed(c1, builder -> builder + .reducedValue(TruthValue.UNKNOWN) + .put(Tuple.of(0), TruthValue.TRUE)) + .seed(c2, builder -> builder + .put(Tuple.of(1), TruthValue.TRUE) + .put(Tuple.of(2), TruthValue.TRUE)) + .seed(entry, builder -> builder + .reducedValue(TruthValue.UNKNOWN) + .put(Tuple.of(0, 1), TruthValue.TRUE) + .put(Tuple.of(0, 2), TruthValue.TRUE)) + .build(); + + var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); + var interpretation = model.getAdapter(ReasoningAdapter.class).getPartialInterpretation(Concreteness.PARTIAL, + entry); + + assertThat(interpretation.get(Tuple.of(0, 0)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(0, 1)), is(TruthValue.TRUE)); + assertThat(interpretation.get(Tuple.of(0, 2)), is(TruthValue.TRUE)); + assertThat(interpretation.get(Tuple.of(1, 0)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(1, 1)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(1, 2)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(2, 0)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(2, 1)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(2, 2)), is(TruthValue.FALSE)); + } + + @Test + void loopTest() { + var modelSeed = ModelSeed.builder(3) + .seed(COUNT_SYMBOL, builder -> builder.reducedValue(CardinalityIntervals.ONE)) + .seed(CONTAINED_SYMBOL, builder -> builder.reducedValue(TruthValue.UNKNOWN)) + .seed(CONTAINS_SYMBOL, builder -> builder.reducedValue(TruthValue.UNKNOWN)) + .seed(c1, builder -> builder.reducedValue(TruthValue.UNKNOWN)) + .seed(c2, builder -> builder + .put(Tuple.of(0), TruthValue.TRUE) + .put(Tuple.of(1), TruthValue.TRUE) + .put(Tuple.of(2), TruthValue.TRUE)) + .seed(entry, builder -> builder + .reducedValue(TruthValue.UNKNOWN) + .put(Tuple.of(0, 1), TruthValue.TRUE) + .put(Tuple.of(1, 2), TruthValue.TRUE) + .put(Tuple.of(2, 0), TruthValue.TRUE)) + .build(); + + var model = store.getAdapter(ReasoningStoreAdapter.class).createInitialModel(modelSeed); + var interpretation = model.getAdapter(ReasoningAdapter.class).getPartialInterpretation(Concreteness.PARTIAL, + entry); + + assertThat(interpretation.get(Tuple.of(0, 0)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(0, 1)), is(TruthValue.ERROR)); + assertThat(interpretation.get(Tuple.of(0, 2)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(1, 0)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(1, 1)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(1, 2)), is(TruthValue.ERROR)); + assertThat(interpretation.get(Tuple.of(2, 0)), is(TruthValue.ERROR)); + assertThat(interpretation.get(Tuple.of(2, 1)), is(TruthValue.FALSE)); + assertThat(interpretation.get(Tuple.of(2, 2)), is(TruthValue.FALSE)); + } +} diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java index 00b0a96d..22ce8962 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/bfs/BFS.java @@ -12,10 +12,7 @@ package tools.refinery.viatra.runtime.rete.itc.alg.misc.bfs; import tools.refinery.viatra.runtime.rete.itc.igraph.IBiDirectionalGraphDataSource; import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; public class BFS { @@ -35,7 +32,7 @@ public class BFS { * @return true if source is reachable from target, false otherwise */ public static boolean isReachable(V source, V target, IGraphDataSource graph) { - List nodeQueue = new ArrayList(); + Deque nodeQueue = new ArrayDeque(); Set visited = new HashSet(); nodeQueue.add(source); @@ -45,17 +42,17 @@ public class BFS { return ret; } - private static boolean _isReachable(V target, IGraphDataSource graph, List nodeQueue, Set visited) { + private static boolean _isReachable(V target, IGraphDataSource graph, Deque nodeQueue, Set visited) { while (!nodeQueue.isEmpty()) { - V node = nodeQueue.remove(0); + V node = nodeQueue.removeFirst(); for (V t : graph.getTargetNodes(node).distinctValues()){ if (t.equals(target)) { return true; } if (!visited.contains(t)) { visited.add(t); - nodeQueue.add(t); + nodeQueue.addLast(t); } } } @@ -65,7 +62,7 @@ public class BFS { public static Set reachableSources(IBiDirectionalGraphDataSource graph, V target) { Set retSet = new HashSet(); retSet.add(target); - List nodeQueue = new ArrayList(); + Deque nodeQueue = new ArrayDeque(); nodeQueue.add(target); _reachableSources(graph, nodeQueue, retSet); @@ -73,14 +70,14 @@ public class BFS { return retSet; } - private static void _reachableSources(IBiDirectionalGraphDataSource graph, List nodeQueue, + private static void _reachableSources(IBiDirectionalGraphDataSource graph, Deque nodeQueue, Set retSet) { while (!nodeQueue.isEmpty()) { - V node = nodeQueue.remove(0); + V node = nodeQueue.removeFirst(); for (V _node : graph.getSourceNodes(node).distinctValues()) { if (!retSet.contains(_node)) { retSet.add(_node); - nodeQueue.add(_node); + nodeQueue.addLast(_node); } } } @@ -89,7 +86,7 @@ public class BFS { public static Set reachableTargets(IGraphDataSource graph, V source) { Set retSet = new HashSet(); retSet.add(source); - List nodeQueue = new ArrayList(); + Deque nodeQueue = new ArrayDeque(); nodeQueue.add(source); _reachableTargets(graph, nodeQueue, retSet); @@ -97,15 +94,15 @@ public class BFS { return retSet; } - private static void _reachableTargets(IGraphDataSource graph, List nodeQueue, Set retSet) { + private static void _reachableTargets(IGraphDataSource graph, Deque nodeQueue, Set retSet) { while (!nodeQueue.isEmpty()) { - V node = nodeQueue.remove(0); + V node = nodeQueue.removeFirst(); for (V _node : graph.getTargetNodes(node).distinctValues()) { if (!retSet.contains(_node)) { retSet.add(_node); - nodeQueue.add(_node); + nodeQueue.addLast(_node); } } } diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java index 5343f956..794dabc0 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/RepresentativeElectionAlgorithm.java @@ -45,6 +45,40 @@ public abstract class RepresentativeElectionAlgorithm implements IGraphObserver< components.put(representative, set); } + protected void merge(Set toMerge) { + if (toMerge.isEmpty()) { + return; + } + var representativesToMerge = new HashSet<>(); + Object bestRepresentative = null; + Set bestSet = null; + for (var object : toMerge) { + var representative = getRepresentative(object); + if (representativesToMerge.add(representative)) { + var component = getComponent(representative); + if (bestSet == null || bestSet.size() < component.size()) { + bestRepresentative = representative; + bestSet = component; + } + } + } + if (bestRepresentative == null) { + throw new AssertionError("Could not determine best representative"); + } + for (var representative : representativesToMerge) { + if (!bestRepresentative.equals(representative)) { + components.remove(representative); + } + } + components.put(bestRepresentative, toMerge); + for (var object : toMerge) { + var previousRepresentative = representatives.put(object, bestRepresentative); + if (!bestSet.contains(object)) { + notifyToObservers(object, previousRepresentative, bestRepresentative); + } + } + } + protected void merge(Object leftRepresentative, Object rightRepresentative) { if (leftRepresentative.equals(rightRepresentative)) { return; diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java index 4acb2b77..0463301b 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/representative/StronglyConnectedComponentAlgorithm.java @@ -35,7 +35,10 @@ public class StronglyConnectedComponentAlgorithm extends RepresentativeElectionA return; } if (BFS.isReachable(target, source, graph)) { - merge(sourceRoot, targetRoot); + var sources = BFS.reachableSources(graph, target); + var targets = BFS.reachableTargets(graph, source); + sources.retainAll(targets); + merge(sources); } } -- cgit v1.2.3-54-g00ecf From d43046735ec9b1dd2b93aba4182cc8b94ee80f1c Mon Sep 17 00:00:00 2001 From: Kristóf Marussy Date: Thu, 31 Aug 2023 17:36:36 +0200 Subject: refactor(viatra): replace Stack with Deque --- .../viatra/runtime/rete/itc/alg/misc/scc/SCC.java | 25 ++++++++++------------ .../itc/alg/misc/topsort/TopologicalSorting.java | 10 ++++----- 2 files changed, 16 insertions(+), 19 deletions(-) (limited to 'subprojects/viatra-runtime-rete') diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java index f6ef9847..de070839 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/scc/SCC.java @@ -9,13 +9,10 @@ package tools.refinery.viatra.runtime.rete.itc.alg.misc.scc; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.Stack; - -import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; import tools.refinery.viatra.runtime.matchers.util.CollectionsFactory; +import tools.refinery.viatra.runtime.rete.itc.igraph.IGraphDataSource; + +import java.util.*; /** * Efficient algorithms to compute the Strongly Connected Components in a directed graph. @@ -52,10 +49,10 @@ public class SCC { Map> notVisitedMap = CollectionsFactory.createMap(); // stores the nodes during the traversal - Stack nodeStack = new Stack(); + Deque nodeStack = new ArrayDeque(); // stores the nodes which belong to an scc (there can be many sccs in the stack at the same time) - Stack sccStack = new Stack(); + Deque sccStack = new ArrayDeque(); boolean sink = false, finishedTraversal = true; @@ -71,14 +68,14 @@ public class SCC { nodeStack.push(n); while (!nodeStack.isEmpty()) { - V currentNode = nodeStack.peek(); + V currentNode = nodeStack.peekLast(); sink = false; finishedTraversal = false; SCCProperty prop = nodeMap.get(currentNode); if (nodeMap.get(currentNode).getIndex() == 0) { index++; - sccStack.push(currentNode); + sccStack.addLast(currentNode); prop.setIndex(index); prop.setLowlink(index); @@ -97,7 +94,7 @@ public class SCC { if (targetNodeMap.get(currentNode).size() == 0) { targetNodeMap.remove(currentNode); - nodeStack.pop(); + nodeStack.removeLast(); for (V targetNode : g.getTargetNodes(currentNode).distinctValues()) { if (notVisitedMap.get(currentNode).contains(targetNode)) { @@ -115,13 +112,13 @@ public class SCC { // and mark it in the notVisitedMap if (nodeMap.get(targetNode).getIndex() == 0) { notVisitedMap.get(currentNode).add(targetNode); - nodeStack.add(targetNode); + nodeStack.addLast(targetNode); } } } // if currentNode has no target nodes else { - nodeStack.pop(); + nodeStack.removeLast(); sink = true; } @@ -131,7 +128,7 @@ public class SCC { V targetNode = null; do { - targetNode = sccStack.pop(); + targetNode = sccStack.removeLast(); sc.add(targetNode); } while (!targetNode.equals(currentNode)); diff --git a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java index db46d178..89be6804 100644 --- a/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java +++ b/subprojects/viatra-runtime-rete/src/main/java/tools/refinery/viatra/runtime/rete/itc/alg/misc/topsort/TopologicalSorting.java @@ -40,15 +40,15 @@ public class TopologicalSorting { public static List compute(final IGraphDataSource gds) { final Set visited = new HashSet(); final LinkedList result = new LinkedList(); - final Stack> dfsStack = new Stack>(); + final Deque> dfsStack = new ArrayDeque>(); for (final T node : gds.getAllNodes()) { if (!visited.contains(node)) { - dfsStack.push(new Pair(node, false)); + dfsStack.addLast(new Pair(node, false)); } while (!dfsStack.isEmpty()) { - final Pair head = dfsStack.pop(); + final Pair head = dfsStack.removeLast(); final T source = head.element; if (head.isParent) { @@ -57,11 +57,11 @@ public class TopologicalSorting { } else { // first time we see source, continue with its children visited.add(source); - dfsStack.push(new Pair(source, true)); + dfsStack.addLast(new Pair(source, true)); for (final T target : gds.getTargetNodes(source).distinctValues()) { if (!visited.contains(target)) { - dfsStack.push(new Pair(target, false)); + dfsStack.addLast(new Pair(target, false)); } } } -- cgit v1.2.3-54-g00ecf